// ------------------------------------------------------------------------------------------
// Licensed by Interprise Solutions.
// http://www.InterpriseSolutions.com
// For details on this license please visit  the product homepage at the URL above.
// THE ABOVE NOTICE MUST REMAIN INTACT.
// ------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Interprise.Extendable.Base.Facade.Customer.CreditCardGateway;
using Interprise.Facade.Base;
using Interprise.Facade.Customer;
using Interprise.Facade.SystemManager;
using Interprise.Framework.Base.DatasetComponent;
using Interprise.Framework.Base.DatasetGateway;
using Interprise.Framework.Base.Shared;
using Interprise.Framework.Customer.DatasetGateway;
using Interprise.Framework.SystemManager.DatasetGateway;
using InterpriseSuiteEcommerceCommon.DataAccess;
using InterpriseSuiteEcommerceCommon.DTO;
using InterpriseSuiteEcommerceCommon.Extensions;
using InterpriseSuiteEcommerceCommon.InterpriseIntegration.Web;

namespace InterpriseSuiteEcommerceCommon.InterpriseIntegration
{
    public class InterpriseShoppingCart : ShoppingCart, IEnumerable, IDisposable
    {
        #region Variable Declaration
        private SalesOrderDatasetGateway _gatewaySalesOrderDataset;
        private SalesOrderFacade _facadeSalesOrder;
        private Customer _customer;
        private string m_countryCode;
        #endregion

        public string CountryCode
        {
            get { return m_countryCode; }
            set { m_countryCode = value; }
        }

        #region Constructor

        private InterpriseShoppingCart() { }

        public InterpriseShoppingCart(Dictionary<string, EntityHelper> entityHelpers,
                                        int skinId,
                                        Customer customer,
                                        CartTypeEnum type,
                                        string originalRecurringOrderNumber,
                                        bool onlyLoadRecurringItemsThatAreDue, bool LoadDatafromDB, string pagename = "")
            : base(entityHelpers, skinId, customer, type, originalRecurringOrderNumber, onlyLoadRecurringItemsThatAreDue, LoadDatafromDB, pagename)
        {
            if (customer.ShippingAddresses.Count > 0)
            {
                CountryCode = customer.ShippingAddresses[0].Country;
            }
            // end

            _customer = customer;
        }

        #endregion

        #region  For Threading Testing Purposes Only

        private InterpriseShoppingCart(Customer ThisCustomer, CartTypeEnum cartType)
        {
            m_ThisCustomer = ThisCustomer;
            m_CartType = cartType;
            this.LoadFromDB(cartType);
        }

        public static InterpriseShoppingCart Get(Customer forThisCustomer, CartTypeEnum withCartType)
        {
            return Get(forThisCustomer, withCartType, false);
        }

        public static InterpriseShoppingCart Get(Customer forThisCustomer, CartTypeEnum withCartType, bool buildSalesOrderDetails)
        {
            InterpriseShoppingCart cart = new InterpriseShoppingCart(forThisCustomer, withCartType);
            if (buildSalesOrderDetails)
            {
                cart.BuildSalesOrderDetails();
            }
            return cart;
        }

        #endregion

        #region Properties

        public SalesOrderDatasetGateway SalesOrderDataset
        {
            get { return _gatewaySalesOrderDataset; }
        }

        public SalesOrderFacade SalesOrderFacade
        {
            get { return _facadeSalesOrder; }
        }

        public decimal ShoppingCartTotalRate
        {
            get 
            {
                if (_gatewaySalesOrderDataset.CustomerSalesOrderView.Count > 0)
                { return _gatewaySalesOrderDataset.CustomerSalesOrderView[0].TotalRate; }
                else
                    return 0;                                            
                  
            }
        }

        public override bool HasCoupon()
        {
            string couponCode = string.Empty;
            return HasCoupon(ref couponCode);
        }

        public bool HasCouponApplied()
        {
            // NOTE : 
            //  In InterpriseSuiteEcommerce we assume the receipt to hold the coupon info
            return _facadeSalesOrder.Receipt != decimal.Zero;
        }

        #endregion

        #region Enumeration

        public enum OrderStatus
        {
            None,
            MaxMindFraudCheckFailed,
            GatewayAuthorizationFailed,
            Completed
        }

        #endregion

        private OrderStatus _status = OrderStatus.None;

        public OrderStatus Status
        {
            get { return _status; }
        }

        #region Methods

        public InterpriseShoppingCart ForAddress(Address thisAddress)
        {
            var newCart = new InterpriseShoppingCart(this.EntityHelpers, this.SkinID, this.ThisCustomer, this.CartType, string.Empty, false, false);
            foreach (CartItem item in this)
            {
                if (item.m_ShippingAddressID == thisAddress.AddressID)
                {
                    newCart.CartItems.Add(item.Copy());
                }
            }

            newCart.BuildSalesOrderDetails();

            return newCart;
        }

        public bool OnlyShippingAddressIsNotCustomerDefault()
        {
            if (this.CartItems.Count == 0 || this.HasMultipleShippingAddresses()) return false;

            return this.FirstItem().m_ShippingAddressID != ThisCustomer.PrimaryShippingAddress.AddressID;
        }

        public List<InterpriseShoppingCart> SplitIntoMultipleOrdersByDifferentShipToAddresses()
        {
            var orders = new List<InterpriseShoppingCart>();
            var nonShipCart = new InterpriseShoppingCart(this.EntityHelpers, this.SkinID, this.ThisCustomer, this.CartType, string.Empty, false, false);
            var itemsWithTheSameShipToAddress = new Dictionary<string, List<CartItem>>();

            foreach (var item in this.CartItems)
            {
                if (item.IsDownload || item.IsService)
                {
                    item.m_ShippingAddressID = ThisCustomer.PrimaryShippingAddress.AddressID;
                    nonShipCart.CartItems.Add(item.Clone<CartItem>());
                }
                else
                {
                    string shipToAddress = item.m_ShippingAddressID;
                    // check if we have already a ky for this item
                    if (!itemsWithTheSameShipToAddress.ContainsKey(shipToAddress))
                    {
                        itemsWithTheSameShipToAddress.Add(shipToAddress, new List<CartItem>());
                    }

                    var items = itemsWithTheSameShipToAddress[shipToAddress];
                    items.Add(item);
                }
            }

            foreach (string uniqueShipToAddress in itemsWithTheSameShipToAddress.Keys)
            {
                var items = itemsWithTheSameShipToAddress[uniqueShipToAddress];
                var newCart = new InterpriseShoppingCart(this.EntityHelpers, this.SkinID, this.ThisCustomer, this.CartType, string.Empty, false, false);

                foreach (CartItem item in items)
                {
                    newCart.CartItems.Add(item.Clone<CartItem>());
                }

                orders.Add(newCart);
            }

            if (nonShipCart.CartItems.Count > 0)
            {
                orders.Add(nonShipCart);
            }

            nonShipCart.MakeShippingNotRequired(false);

            return orders;
        }

        #region BuildSalesOrderDetails

        public void BuildSalesOrderDetails(string couponCode = "")
        {
            BuildSalesOrderDetails(true, true, couponCode);
        }

        public bool HasShippableComponents()
        {
            return !this.IsNoShippingRequired();
        }

        #region MakeShippingNotRequired

        public void MakeShippingNotRequired()
        {
            MakeShippingNotRequired(true);
        }

        public void MakeShippingNotRequired(bool updateCart)
        {
            EnsureNoShippingMethodRequiredIsExisting();
            EnsureNoShippingMethodRequiredIsAssociatedWithCurrentCustomer();

            string shippingMethod = AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder");

            CartItems.ForEach(item => item.m_ShippingMethod = shippingMethod);

            if (!updateCart) return;
            
            SetCartShippingMethod(shippingMethod);
        }

        #endregion

        #region EnsureNoShippingMethodRequiredIsExisting

        private void EnsureNoShippingMethodRequiredIsAssociatedWithCurrentCustomer()
        {
            bool noShippingMethodRequiredShippingMethodIsExistingButNotAssociatedWithTheShippingMethodGroup = false;
            string noShippingRequiredCode = AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder");
            string shippingMethodGroupToAssociateWith = string.Empty;

            if (ThisCustomer.IsRegistered)
            {
                shippingMethodGroupToAssociateWith = ThisCustomer.PrimaryShippingAddress.ShippingMethodGroup;
            }
            else
            {
                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, "SELECT ShippingMethodGroup FROM CustomerShipTo WITH (NOLOCK) WHERE ShipToCode = {0}", DB.SQuote(ThisCustomer.AnonymousShipToCode)))
                    {
                        if (reader.Read())
                        {
                            shippingMethodGroupToAssociateWith = DB.RSField(reader, "ShippingMethodGroup");
                        }
                    }
                }
            }

            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, "SELECT ShippingMethodCode FROM SystemShippingMethodGroupDetail with (NOLOCK) WHERE ShippingMethodGroup = {0} AND ShippingMethodCode = {1}", DB.SQuote(shippingMethodGroupToAssociateWith), DB.SQuote(noShippingRequiredCode)))
                {
                    noShippingMethodRequiredShippingMethodIsExistingButNotAssociatedWithTheShippingMethodGroup = !reader.Read();
                }
            }
            
            if (!noShippingMethodRequiredShippingMethodIsExistingButNotAssociatedWithTheShippingMethodGroup) return;
            
            // Associate this shipping method to ALL groups
            // So that it will be available to all customers when it comes migrating sales orders..
            using (var gatewayShippingMethodGroup = new ShippingMethodGroupDatasetGateway())
            {
                using (var facadeShippingMethodGroup = new ShippingMethodGroupFacade(gatewayShippingMethodGroup))
                {
                    string[][] commandSet = facadeShippingMethodGroup.get_LoadCommandSet(ThisCustomer.PrimaryShippingAddress.ShippingMethodGroup);
                    facadeShippingMethodGroup.LoadDataSet(
                        commandSet,
                        Interprise.Framework.Base.Shared.Enum.ClearType.Specific,
                        Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online
                    );

                    if (gatewayShippingMethodGroup.SystemShippingMethodGroup.Count == 1)
                    {
                        var detailRow = gatewayShippingMethodGroup.SystemShippingMethodGroupDetailView.NewSystemShippingMethodGroupDetailViewRow();
                        detailRow.BeginEdit();
                        detailRow.ShippingMethodGroup = ThisCustomer.PrimaryShippingAddress.ShippingMethodGroup;
                        detailRow.ShippingMethodCode = noShippingRequiredCode;
                        detailRow.FreightChargeType = Interprise.Framework.Base.Shared.Const.CALCULATE_FREIGHT_CHARGE_NONE;
                        detailRow.SystemShippingMethodGroupRow = gatewayShippingMethodGroup.SystemShippingMethodGroup[0];
                        detailRow.EndEdit();

                        gatewayShippingMethodGroup.SystemShippingMethodGroupDetailView.AddSystemShippingMethodGroupDetailViewRow(detailRow);

                        facadeShippingMethodGroup.UpdateDataSet();
                    }
                }
            }
        }

        public static void EnsureNoShippingMethodRequiredIsExisting()
        {
            bool noShippingMethodRequiredShippingMethodExisting = false;

            string noShippingRequiredCode = AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder");

            if (CommonLogic.IsStringNullOrEmpty(noShippingRequiredCode))
            {
                throw new Exception("ShippingMethodCodeZeroDollarOrder AppConfig is not configured. Please setup a dummy shipping method code to mark orders that doesn't require shipping!!!");
            }


            using (SqlConnection con = DB.NewSqlConnection())
            {
                con.Open();
                using (IDataReader reader = DB.GetRSFormat(con, "SELECT ShippingMethodCode FROM SystemShippingMethod with (NOLOCK) WHERE ShippingMethodCode = {0}", DB.SQuote(noShippingRequiredCode)))
                {
                    noShippingMethodRequiredShippingMethodExisting = reader.Read();
                }
            }

            if (!noShippingMethodRequiredShippingMethodExisting)
            {
                throw new Exception(string.Format("ShippingMethodCodeZeroDollarOrder AppConfig:\"{0}\" is not existing in the database!!!", noShippingRequiredCode));
            }
        }

        #endregion

        public void MakePaymentTermNotRequired()
        {
            EnsureNoPaymentTermRequiredIsExisting();
            EnsureNoPaymentTermRequiredIsAssociatedWithCurrentCustomer();

            InterpriseHelper.UpdateCustomerPaymentTerm(ThisCustomer, AppLogic.AppConfig("PaymentTermCodeZeroDollarOrder"));
        }

        private void EnsureNoPaymentTermRequiredIsAssociatedWithCurrentCustomer()
        {
            string noPaymentPaymentTermCode = AppLogic.AppConfig("PaymentTermCodeZeroDollarOrder");
            bool noPaymentTermRequiredExistingAndIsAssociatedWithCustomerPaymentTermGroup = false;

            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, "SELECT PaymentTermCode FROM SystemPaymentTermGroupDetail with (NOLOCK) WHERE PaymentTermCode = {0} AND PaymentTermGroup = {1}", DB.SQuote(noPaymentPaymentTermCode), DB.SQuote(ThisCustomer.PaymentTermGroup)))
                {
                    noPaymentTermRequiredExistingAndIsAssociatedWithCustomerPaymentTermGroup = reader.Read();
                }
            }

            if (!noPaymentTermRequiredExistingAndIsAssociatedWithCustomerPaymentTermGroup)
            {
                // Associate this shipping method to ALL groups
                // So that it will be available to all customers when it comes migrating sales orders..
                using (var gatewayPaymentTermGroup = new PaymentTermGroupDatasetGateway())
                {
                    using (var facadePaymentTermGroup = new PaymentTermGroupFacade(gatewayPaymentTermGroup))
                    {
                        string[][] commandSet = facadePaymentTermGroup.get_LoadCommandSet(ThisCustomer.PaymentTermGroup);
                        facadePaymentTermGroup.LoadDataSet(
                            commandSet,
                            Interprise.Framework.Base.Shared.Enum.ClearType.Specific,
                            Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online
                        );

                        if (gatewayPaymentTermGroup.SystemPaymentTermGroup.Count == 1)
                        {
                            PaymentTermGroupDatasetGateway.SystemPaymentTermGroupDetailViewRow detailRow = gatewayPaymentTermGroup.SystemPaymentTermGroupDetailView.NewSystemPaymentTermGroupDetailViewRow();
                            detailRow.BeginEdit();
                            detailRow.PaymentTermGroup = ThisCustomer.PaymentTermGroup;
                            detailRow.PaymentTermCode = noPaymentPaymentTermCode;
                            detailRow.PaymentTermDescription = noPaymentPaymentTermCode;
                            detailRow.SystemPaymentTermGroupRow = gatewayPaymentTermGroup.SystemPaymentTermGroup[0];
                            detailRow.EndEdit();

                            gatewayPaymentTermGroup.SystemPaymentTermGroupDetailView.AddSystemPaymentTermGroupDetailViewRow(detailRow);

                            facadePaymentTermGroup.UpdateDataSet();
                        }
                    }
                }
            }
        }

        public static bool IsWebCheckOutIncluded(string paymentTermCode)
        {
            using (var gatewayPaymentTermGroup = new PaymentTermGroupDatasetGateway())
            {
                using (var facadePaymentTermGroup = new PaymentTermGroupFacade(gatewayPaymentTermGroup))
                {
                    string[][] commandSet = facadePaymentTermGroup.get_LoadCommandSet(Customer.Current.PaymentTermGroup);
                    facadePaymentTermGroup.LoadDataSet(
                        commandSet,
                        Interprise.Framework.Base.Shared.Enum.ClearType.Specific,
                        Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online
                    );
                    return (gatewayPaymentTermGroup.SystemPaymentTermGroupDetailView.FindByPaymentTermGroupPaymentTermCode(Customer.Current.PaymentTermGroup, paymentTermCode) != null);
                }
            }
        }
        

        public static void EnsureNoPaymentTermRequiredIsExisting()
        {
            bool noPaymentTermRequiredPaymentTermExisting = false;
            string noPaymentPaymentTermCode = AppLogic.AppConfig("PaymentTermCodeZeroDollarOrder");

            if (CommonLogic.IsStringNullOrEmpty(noPaymentPaymentTermCode))
            {
                throw new Exception("PaymentTermCodeZeroDollarOrder AppConfig is not configured. Please setup a dummy payment term code to mark orders that doesn't require payment!!!");
            }

            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, "SELECT PaymentTermCode FROM SystemPaymentTerm with (NOLOCK) WHERE PaymentTermCode = {0}", DB.SQuote(noPaymentPaymentTermCode)))
                {
                    noPaymentTermRequiredPaymentTermExisting = reader.Read();
                }
            }

            if (!noPaymentTermRequiredPaymentTermExisting)
            {
                throw new Exception(string.Format("PaymentTermCodeZeroDollarOrder AppConfig:\"{0}\" is not existing in the database!!!", noPaymentPaymentTermCode));
            }
        }

        private void ComputeFreightCharge(bool clear, decimal exchangeRate = 0)
        {
            if (clear)
            {
                // zero out the freight so it won't alter the computation of the coupon discount
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShippingMethodCode = string.Empty;
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].Freight = decimal.Zero;
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].FreightRate = decimal.Zero;
            }
            else
            {
                // first check if the shipping method is real-time
                string shippingMethodCode = this.FirstItem().m_ShippingMethod;
                if (shippingMethodCode.IsNullOrEmptyTrimmed()) return;
                
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShippingMethod = shippingMethodCode;
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShippingMethodCode = shippingMethodCode;

                string couponCode = string.Empty;
                bool hasCoupon = HasCoupon(ref couponCode);

                //check if no shippable Components, check if free shipping threshold is setup, check if coupon is free shipping
                if ((!this.HasShippableComponents()) ||
                        (InterpriseHelper.ShippingMethodCodeBelongsToFreeShippingMethodList(shippingMethodCode) &&
                            InterpriseHelper.IsFreeShippingThresholdEnabled(this.GetCartSubTotal())) ||
                        (hasCoupon && IsCouponHasFreeShipping(couponCode)))                
                {
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].Freight = decimal.Zero;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].FreightRate = decimal.Zero;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].IsFreightOverwrite = true;
                }
                else
                {

                    exchangeRate = (exchangeRate == 0) ? _facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode) : exchangeRate;

                    decimal freight = decimal.Zero;
                    decimal freightRate = decimal.Zero;

                    var shippingAddress = ThisCustomer.PrimaryShippingAddress;
                    var shippingMethod = GetShippingMethods(shippingAddress, shippingMethodCode);

                    if (shippingMethod != null && shippingMethod.Count > 0)
                    {                            
                        for (int i = 0; i < shippingMethod.Count; i++)
                        {
                            if (i == 0 || shippingMethod[i].ForOversizedItem)
                            { freightRate += shippingMethod[i].Freight; }
                        }
                            
                        freight = _facadeSalesOrder.ConvertCurrency(exchangeRate, freightRate, true, ThisCustomer.CurrencyCode);
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].Freight = freight;
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].FreightRate = freightRate;
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].IsFreightOverwrite = true;
                    }

                    _facadeSalesOrder.ComputeFreight();                        
                }
            }
        }

        #endregion

        private void AssignKitItem(DataRow itemKitRow, DataRowView lineItemRow, Guid cartId)
        {
            var kitDataset = new ItemKitDatasetGateway();
            var kitFacade = new ItemKitFacade(kitDataset);

            string currencyCode = ThisCustomer.CurrencyCode;

            kitFacade.ExchangeRate = _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ExchangeRate;
            bool isCurrencyIncludedForInventorySelling = InterpriseHelper.IsCurrencyIncludedForInventorySelling(currencyCode);

            // Special case 2 
            //  Currency is added in Inventory Selling Currency late
            //  after the kit has been created, ideally the kit should regenerate kit pricing for this currency
            //  but for the meantime, we should handle this by looking into the home currency
            bool currencyIsIncludedInInventorySellingCurrencyButHasNoKitPricingDetailYetForThisItem =
            InterpriseHelper.CurrencyIsIncludedInInventorySellingCurrencyButHasNoKitPricingDetailYet(currencyCode, itemKitRow["ItemCode"].ToString());

            string kitDetailQuery =
                string.Format(
                    "exec EcommerceGetWebKitCartDetail @ItemKitCode = {0}, @CurrencyCode = {1}, @LanguageCode = {2}, @CustomerCode = {3}, @CartID = {4}, @ContactCode = {5}",
                    DB.SQuote(itemKitRow["ItemCode"].ToString()),
                    DB.SQuote(CommonLogic.IIF((isCurrencyIncludedForInventorySelling && !currencyIsIncludedInInventorySellingCurrencyButHasNoKitPricingDetailYetForThisItem), currencyCode, Currency.GetHomeCurrency())),
                    DB.SQuote(ThisCustomer.LocaleSetting),
                    DB.SQuote(ThisCustomer.CustomerCode),
                    DB.SQuote(cartId.ToString()),
                    DB.SQuote(ThisCustomer.ContactCode)
                );


            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, kitDetailQuery))
                {
                    while (reader.Read())
                    {
                        ItemKitDatasetGateway.KitConfiguratorRow kitConfigRow;
                        kitConfigRow = kitDataset.KitConfigurator.NewKitConfiguratorRow();

                        kitConfigRow.BeginEdit();
                        kitConfigRow._Select = true;
                        kitConfigRow.ItemName = DB.RSField(reader, "ItemName");
                        kitConfigRow.ItemType = DB.RSField(reader, "ItemType");
                        kitConfigRow.GroupType = DB.RSField(reader, "GroupType");
                        kitConfigRow.ItemCode = DB.RSField(reader, "ItemCode");
                        kitConfigRow.ItemDescription = DB.RSField(reader, "ItemDescription");
                        kitConfigRow.DisplayImage = new byte[] { };
                        kitConfigRow.GroupCode = DB.RSField(reader, "GroupCode");
                        kitConfigRow.Quantity = Convert.ToDecimal(DB.RSFieldDecimal(reader, "Quantity"));
                        kitConfigRow.UnitMeasureCode = DB.RSField(reader, "UnitMeasureCode");
                        kitConfigRow.UnitMeasureQty = Convert.ToInt32(DB.RSFieldDecimal(reader, "UnitMeasureQuantity"));

                        kitConfigRow.SalesPrice = DB.RSFieldDecimal(reader, "Total");
                        kitConfigRow.SalesPriceRate = DB.RSFieldDecimal(reader, "TotalRate");

                        if (isCurrencyIncludedForInventorySelling &&
                            currencyIsIncludedInInventorySellingCurrencyButHasNoKitPricingDetailYetForThisItem)
                        {
                            decimal convertedRate = kitFacade.ConvertCurrency(kitFacade.ExchangeRate, DB.RSFieldDecimal(reader, "Total"), false, currencyCode, Interprise.Framework.Base.Shared.Enum.CurrencyFormat.Total);
                            kitConfigRow.SalesPriceRate = convertedRate;
                        }

                        kitConfigRow.ExtSalesPrice = kitConfigRow.SalesPriceRate;
                        kitConfigRow.ExtSalesPriceRate = kitConfigRow.SalesPriceRate;

                        kitConfigRow.SalesPriceDifference = decimal.Zero;
                        kitConfigRow.SalesPriceDifferenceRate = decimal.Zero;

                        if (!kitConfigRow.IsSalesTaxCodeNull() &&
                            !string.IsNullOrEmpty(kitConfigRow.SalesTaxCode))
                        {
                            using (var con2 = DB.NewSqlConnection())
                            {
                                con.Open();
                                using (var taxReader = DB.GetRSFormat(con2, "SELECT TaxDescription FROM SystemTaxScheme with (NOLOCK) WHERE TaxCode = {0}",
                                                                                    DB.SQuote(kitConfigRow.SalesTaxCode)))
                                {
                                    if (taxReader.Read())
                                    {
                                        kitConfigRow.TaxDescription =
                                            (taxReader["TaxDescription"] != null &&
                                                taxReader["TaxDescription"] != DBNull.Value &&
                                                ((string)taxReader["TaxDescription"]).IsNullOrEmptyTrimmed()
                                                ) ? (string)taxReader["TaxDescription"] : string.Empty;
                                    }
                                }
                            }
                        }

                        kitConfigRow.EndEdit();

                        kitDataset.KitConfigurator.AddKitConfiguratorRow(kitConfigRow);
                    }
                }
            }

            kitFacade.ComputePercentage();
            kitFacade.ComputeKitItemsSalesPrice(kitFacade.ComputeTotal());

            string errMsg = string.Empty;
            _facadeSalesOrder.AssignItemKit(
                itemKitRow,
                kitFacade.KitItems,
                lineItemRow,
                Interprise.Framework.Base.Shared.Const.KIT_DISPLAY_KIT_PRICE,
                Convert.ToInt32(lineItemRow["QuantityOrdered"]),
                kitFacade.ComputeTotal(),
                Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder,
                ref errMsg,
                string.Empty
            );

            // **************************************************
            //  Explicit Disposal and dereferencing goes here...
            // **************************************************
            kitDataset.Dispose();
            kitFacade.Dispose();

            kitDataset = null;
            kitFacade = null;
        }

        public bool HasCoupon(ref string couponCode)
        {
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.CustomerID);
            return InterpriseHelper.GetCustomerCouponIfAny(customerCode, ref couponCode, ThisCustomer.IsRegistered);
        }

        public string RenderHTMLLiteral(IShoppingCartHTMLLiteralRenderer renderer)
        {
            var output = new StringBuilder();
            renderer.Render(this, ref output);
            return output.ToString();
        }

        public bool IsCouponValid(Customer ThisCustomer, string couponCode, ref string errorMessage)
        {
            bool isValid = false;
            var thisCoupon = new CouponStruct();
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.AnonymousCustomerCode);
            // validate the coupon
            string couponQuery =
            string.Format("SELECT * FROM eCommerceValidCouponView with (NOLOCK) WHERE CustomerCode = {0} AND CouponCode = {1} AND ShortString = {2} AND {3} BETWEEN StartingDate AND ExpirationDate",
                DB.SQuote(customerCode),
                DB.SQuote(couponCode),
                DB.SQuote(ThisCustomer.LocaleSetting),
                DB.SQuote(Localization.ToDBDateTimeString(DateTime.Today))
            );


            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, couponQuery))
                {
                    if (reader.Read())
                    {
                        // initially we have a valid coupon just because it exists.
                        isValid = true;

                        thisCoupon.m_Code = DB.RSField(reader, "CouponCode");
                        thisCoupon.m_Description = DB.RSField(reader, "Description");

                        switch (DB.RSField(reader, "CouponType").ToLowerInvariant())
                        {
                            case "products":
                                thisCoupon.m_CouponType = CouponTypeEnum.ProductCoupon;
                                break;
                            case "orders":
                                thisCoupon.m_CouponType = CouponTypeEnum.OrderCoupon;
                                break;
                        }

                        switch (DB.RSField(reader, "DiscountType").ToLowerInvariant())
                        {
                            case "percent":
                                thisCoupon.m_DiscountType = CouponDiscountTypeEnum.PercentDiscount;
                                break;
                            case "amount":
                                thisCoupon.m_DiscountType = CouponDiscountTypeEnum.AmountDiscount;
                                break;
                        }

                        thisCoupon.m_DiscountAmount = DB.RSFieldDecimal(reader, "DiscountAmount");
                        thisCoupon.m_DiscountPercent = ((Decimal)DB.RSFieldSingle(reader, "DiscountPercent"));

                        thisCoupon.m_IncludesFreeShipping = DB.RSFieldBool(reader, "DiscountIncludesFreeShipping");
                        thisCoupon.m_IncludesAllCustomer = DB.RSFieldBool(reader, "IncludeAllCustomer");
                        thisCoupon.m_IncludesAllProduct = DB.RSFieldBool(reader, "IncludeAllProduct");

                        switch (DB.RSFieldInt(reader, "CouponUsage"))
                        {
                            case 1:
                                thisCoupon.m_ExpiresOnFirstUseByAnyCustomer = true;
                                thisCoupon.m_ExpiresAfterOneUsageByEachCustomer = false;
                                thisCoupon.m_ExpiresAfterNUses = 0;
                                break;
                            case 2:
                                thisCoupon.m_ExpiresOnFirstUseByAnyCustomer = false;
                                thisCoupon.m_ExpiresAfterOneUsageByEachCustomer = true;
                                thisCoupon.m_ExpiresAfterNUses = 0;
                                break;
                            case 3:
                                thisCoupon.m_ExpiresOnFirstUseByAnyCustomer = false;
                                thisCoupon.m_ExpiresAfterOneUsageByEachCustomer = false;
                                thisCoupon.m_ExpiresAfterNUses = DB.RSFieldInt(reader, "ExpiresAfterNUses");
                                break;
                        }

                        thisCoupon.m_RequiresMinimumOrderAmount = DB.RSFieldDecimal(reader, "RequiresMinimumOrderAmount");
                        thisCoupon.m_ValidForThisEntity = DB.RSField(reader, "ItemCategory");
                        thisCoupon.m_NumUses = DB.RSFieldInt(reader, "TotalUsageCount");

                        // now let's (really) validate this coupon 
                        errorMessage = ValidateNewCoupon(thisCoupon, ref isValid);

                    }
                    else
                    {
                        // coupon is invalid or expired
                        errorMessage = AppLogic.GetString("shoppingcart.cs.35", 1, ThisCustomer.LocaleSetting);
                    }
                }
            }

            return isValid;
        }

        public String ValidateNewCoupon(CouponStruct coupon, ref bool isValid)
        {
            string status = string.Empty;
            decimal cartSubTotal = decimal.Zero;
            var ThisCustomer = Customer.Current;
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.AnonymousCustomerCode);
            
            isValid = true;
            cartSubTotal = CartItems.Sum(item => item.Price);

            if (coupon.m_ExpiresOnFirstUseByAnyCustomer && Customer.AnyCustomerHasUsedCoupon(coupon.m_Code))
            {
                status = AppLogic.GetString("shoppingcart.cs.14", 1, ThisCustomer.LocaleSetting);
                isValid = false;
            }

            if (coupon.m_ExpiresAfterOneUsageByEachCustomer && Customer.HasUsedCoupon(customerCode, coupon.m_Code))
            {
                status = AppLogic.GetString("shoppingcart.cs.15", 1, ThisCustomer.LocaleSetting);
                isValid = false;
            }

            if (coupon.m_ExpiresAfterNUses > 0 && coupon.m_NumUses > coupon.m_ExpiresAfterNUses)
            {
                status = AppLogic.GetString("shoppingcart.cs.16", 1, ThisCustomer.LocaleSetting);
                isValid = false;
            }

            if (coupon.m_RequiresMinimumOrderAmount > System.Decimal.Zero && cartSubTotal < coupon.m_RequiresMinimumOrderAmount)
            {
                status = String.Format(AppLogic.GetString("shoppingcart.cs.17", 1, ThisCustomer.LocaleSetting), ThisCustomer.FormatBasedOnMyCurrency(coupon.m_RequiresMinimumOrderAmount));
                isValid = false;
            }

            if (!coupon.m_IncludesAllCustomer && !Customer.CustomerCanAvailOfThisCoupon(customerCode, coupon.m_Code))
            {
                status = AppLogic.GetString("shoppingcart.cs.18", 1, ThisCustomer.LocaleSetting);
                isValid = false;
            }

            try
            {
                if (coupon.m_CouponType == CouponTypeEnum.ProductCoupon)
                {
                    int validItem = 0;
                    foreach (CartItem item in CartItems)
                    {
                        if (CouponIsValidForThisProduct(item.ItemCode, coupon.m_Code))
                        {
                            validItem += 1;
                        }
                    }

                    if (validItem == 0)
                    {
                        status = AppLogic.GetString("shoppingcart.cs.19", 1, ThisCustomer.LocaleSetting);
                        isValid = false;
                    }
                }

            }
            catch
            {
                status = AppLogic.GetString("shoppingcart.cs.20", 1, ThisCustomer.LocaleSetting);
                isValid = false;
            }

            return status;
        }

        public bool CouponIsValidForThisProduct(String itemCode, String couponCode)
        {
            bool validForThisProduct = false;
            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var rs = DB.GetRSFormat(con, "exec eCommerceCanItemAvailOfCouponDiscount @ItemCode={0}, @CouponCode={1}", DB.SQuote(itemCode), DB.SQuote(couponCode)))
                {
                    if (rs.Read())
                    {
                        validForThisProduct = DB.RSFieldBool(rs, "CanAvail");
                    }
                }
            }

            return validForThisProduct;
        }

        public bool CouponIncludesFreeShipping(string couponCode = "")
        {
            bool includesFreeShipping = false;
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.AnonymousCustomerCode);

            if (!couponCode.IsNullOrEmptyTrimmed())
            {
                string errorMessage = string.Empty;
                if (IsCouponValid(ThisCustomer, couponCode, ref errorMessage))
                {
                    string couponQuery =
                    string.Format("SELECT * FROM eCommerceValidCouponView with (NOLOCK) WHERE CustomerCode = {0} AND CouponCode = {1} AND ShortString = {2} AND DiscountIncludesFreeShipping = 1 AND {3} BETWEEN StartingDate AND ExpirationDate",
                    DB.SQuote(customerCode),
                    DB.SQuote(couponCode),
                    DB.SQuote(ThisCustomer.LocaleSetting),
                    DB.SQuote(Localization.ToDBDateTimeString(DateTime.Today))
                    );

                    using (var con = DB.NewSqlConnection())
                    {
                        con.Open();
                        using (var reader = DB.GetRSFormat(con, couponQuery))
                        {
                            includesFreeShipping = reader.Read();
                            this.ShippingIsFree = true;
                        }
                    }
                }
            }

            return includesFreeShipping;
        }

        public void ApplyCoupon(string couponCode)
        {
            //if (ThisCustomer.IsNotRegistered) throw new InvalidOperationException("Coupon not applicable for anonymous customers!");

            string errorMessage = string.Empty;
            if (!IsCouponValid(ThisCustomer, couponCode, ref errorMessage))
            {
                throw new InvalidOperationException(errorMessage);
            }

            // NOTE :
            //  Since the EcommerceShoppingCart table only stores entries for inventory items
            //  we'll just use the Customer table as the header and store the coupon info
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.CustomerID);
            string saveCustomerCouponStatement;
            if (ThisCustomer.IsRegistered)
            {
                saveCustomerCouponStatement = string.Format("UPDATE Customer SET CouponCode = {0} WHERE CustomerCode = {1}", DB.SQuote(couponCode), DB.SQuote(customerCode));
            }
            else
            {
                saveCustomerCouponStatement = string.Format("UPDATE EcommerceCustomer SET CouponCode = {0} WHERE CustomerCode = {1}", DB.SQuote(couponCode), DB.SQuote(customerCode));
            }
            DB.ExecuteSQL(saveCustomerCouponStatement);

            if (this.IsSalesOrderDetailBuilt)
            {
                // apply...
                var couponDataset = new DataSet();
                var facadeList = new ListControlFacade();
                var baseCouponDataset = new BaseDataset();

                //deadlock
                int nooftries = 0;
                while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
                {
                    try
                    {
                        if (ThisCustomer.IsRegistered)
                        {
                            facadeList.ReadSearchResultsData("CustomerCouponView",
                                string.Format("CustomerCode = {0} AND CouponCode = {1}",
                                    DB.SQuote(customerCode),
                                    DB.SQuote(couponCode)
                                ),
                                1,
                                false,
                                string.Empty,
                                ref baseCouponDataset,
                                true);
                        }
                        break;
                    }
                    catch (SqlException ex)
                    {
                        if (ex.ErrorCode == 1205)
                        {
                            nooftries += 1;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                if (ThisCustomer.IsRegistered)
                {
                    if (baseCouponDataset.Tables["CustomerCouponView"] != null &&
                        baseCouponDataset.Tables["CustomerCouponView"].Rows.Count >= 1)
                    {
                        DataRow couponRow = baseCouponDataset.Tables["CustomerCouponView"].Rows[0];
                        _facadeSalesOrder.AssignCoupon(couponRow, _gatewaySalesOrderDataset.CustomerSalesOrderView[0]);
                    }
                }
                else
                {
                    string getAnonCouponRow = string.Format("exec EcommerceGetAnonCustomerCoupon @CustomerID = {0}, @CouponCode = {1}, @LanguageCode = {2}",
                                              DB.SQuote(customerCode), DB.SQuote(couponCode), DB.SQuote(ThisCustomer.LanguageCode));
                    DataTable dt = DB.GetDS(getAnonCouponRow, false).Tables[0];
                    if (dt.Rows.Count >= 1)
                    {
                        _facadeSalesOrder.AssignCoupon(dt.Rows[0], _gatewaySalesOrderDataset.CustomerSalesOrderView[0]);
                    }
                }
                

                couponDataset.Dispose();
                baseCouponDataset.Dispose();
                facadeList.Dispose();

                couponDataset = null;
                baseCouponDataset = null;
                facadeList = null;
            }
        }

        public void SetCartShippingMethod(string shippingMethodCode)
        {
            SetCartShippingMethod(shippingMethodCode, string.Empty);
        }

        public void SetCartShippingMethod(string shippingMethodCode, string shippingaddress)
        {
            DB.ExecuteSQL(
                String.Format("exec EcommerceApplyShippingMethodToCart @CustomerCode = {0}, @ShippingMethodCode = {1}, @ShippingAddressID = {2}, @ContactCode = {3}",
                DB.SQuote(ThisCustomer.CustomerCode), DB.SQuote(shippingMethodCode), DB.SQuote(shippingaddress),
                DB.SQuote(ThisCustomer.ContactCode))
            );

            foreach (CartItem item in CartItems)
            {
                item.m_ShippingMethod = shippingMethodCode;
            }
        }

        public override void SetItemQuantity(int cartRecordID, decimal Quantity)
        {
            base.SetItemQuantity(cartRecordID, Quantity);            
        }

        public bool IsSalesOrderDetailBuilt
        {
            get { return _gatewaySalesOrderDataset != null && _facadeSalesOrder != null; }
        }

        public ShippingMethodDTOCollection GetShippingMethods(Address preferredShippingAddress)
        {
            return GetShippingMethods(preferredShippingAddress, string.Empty);
        }

        public ShippingMethodDTOCollection GetShippingMethods(Address preferredShippingAddress, Guid? giftRegistryId = null)
        {
            return GetShippingMethods(preferredShippingAddress, string.Empty, giftRegistryId);
        }

        public ShippingMethodDTOCollection GetShippingMethods(Address preferredShippingAddress, string shippingMethodCode, Guid? giftRegistryId = null)
        {
            var availableShippingMethods = new ShippingMethodDTOCollection();

            if (IsEmpty()) return availableShippingMethods;

            var shipments = GetShipmentComposition(preferredShippingAddress, false);

            string forCustomer = ThisCustomer.CustomerCode;
            if (ThisCustomer.IsNotRegistered)
            {
                forCustomer = ThisCustomer.AnonymousCustomerCode;
            }

            decimal getExchangeRate = _facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode);
            string contactCode = (ThisCustomer.IsNotRegistered) ? "NULL" : ThisCustomer.ContactCode.ToDbQuote();

            if (shipments.Count > 0)
            {

                //Load the giftregistry owner info if giftregistry id is available.
                if(giftRegistryId.HasValue)
                {
                    var registryCustomerInfo = CustomerDA.GetCustomerInfoByRegistryID(giftRegistryId);
                    forCustomer = registryCustomerInfo.CustomerCode;
                    contactCode = registryCustomerInfo.ContactCode.ToDbQuote();
                }

                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, string.Format("exec EcommerceGetCustomerShippingMethodList @CustomerCode = {0}, @IsFromFreeShippingCoupon = {1}, @ContactCode = {2}, @ShippingMethodCode = {3}, @ShipToCode = {4}, @AddressType = {5}, @IsAnonymous = {6}, @CountryCode = {7}, @BusinessType = {8}",
                                                                            (ThisCustomer.IsNotRegistered) ? "NULL" : forCustomer.ToDbQuote(),
                                                                            (IsCouponHasFreeShipping(_gatewaySalesOrderDataset.CustomerSalesOrderView[0][_gatewaySalesOrderDataset.CustomerSalesOrderView.CouponCodeColumn.ColumnName].ToString())) ? 1 : 0,
                                                                            contactCode, 
                                                                            (!shippingMethodCode.IsNullOrEmptyTrimmed()) ? shippingMethodCode.ToDbQuote() : "NULL",
                                                                            (ThisCustomer.IsNotRegistered) ? "NULL" : preferredShippingAddress.AddressID.ToDbQuote(),
                                                                            DB.SQuote(preferredShippingAddress.ResidenceType.ToString()),
                                                                            Convert.ToInt32(ThisCustomer.IsNotRegistered),
                                                                            preferredShippingAddress.Country.ToDbQuote(),
                                                                            ThisCustomer.DefaultPrice.ToDbQuote())))
                    {

                        while (reader.Read())
                        {
                            var shippingMethod = new ShippingMethodDTO(DB.RSField(reader, "ShippingMethodCode"), DB.RSField(reader, "ShippingMethodDescription"));

                            if (shippingMethod.Code == AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder")) continue;
                                
                            shippingMethod.IsDefault = DB.RSFieldBool(reader, "IsDefault");
                            shippingMethod.CarrierCode = DB.RSField(reader, "CarrierCode");
                            shippingMethod.CarrierDescription = DB.RSField(reader, "CarrierDescription");
                            shippingMethod.PackagingType = DB.RSField(reader, "PackagingType");
                            shippingMethod.ServiceType = DB.RSField(reader, "ServiceType");
                            shippingMethod.FreightCalculation = DB.RSFieldInt(reader, "FreightCalculation");
                            shippingMethod.ChargeType = DB.RSFieldInt(reader, "ChargeType");
                            shippingMethod.MiscAmount = DB.RSFieldDecimal(reader, "MiscAmount");
                            shippingMethod.MiscAmount = _facadeSalesOrder.ConvertCurrency(getExchangeRate, shippingMethod.MiscAmount);
                            shippingMethod.Length = DB.RSFieldInt(reader, "Length");
                            shippingMethod.Width = DB.RSFieldInt(reader, "Width");
                            shippingMethod.Height = DB.RSFieldInt(reader, "Height");
                            shippingMethod.WeightThreshold = DB.RSFieldDecimal(reader, "WeightThreshold");
                            availableShippingMethods.Add(shippingMethod);
                        }
                    }
                }
            }

            var rateGateway = new Interprise.Framework.Base.DatasetGateway.Shipping.CarrierDatasetGateway();
            var rateFacade = new Interprise.Facade.Base.Shipping.RateFacade(rateGateway);

            string[] warehouseInfo = null;
            string warehouseCode = string.Empty, isoCode = string.Empty, state = string.Empty, postalCode = string.Empty;
            bool IsResidential = true;

            if (ThisCustomer.IsRegistered)
            {
                warehouseInfo = rateFacade.GetRow(new string[] { "WarehouseCode", "ISOCode", "State", "PostalCode", "AddressType" },
                    "InventoryWarehouse IW (NOLOCK) INNER JOIN SystemCountry SC (NOLOCK) ON IW.Country = SC.CountryCode",
                    "IW.WarehouseCode = (SELECT TOP 1 WarehouseCode FROM CustomerShipTo WHERE ShipToCode IN (SELECT TOP 1 DefaultShippingCode FROM CRMContact WHERE EntityCode = "
                    + DB.SQuote(forCustomer) + " AND ContactCode = " + ((ThisCustomer.IsRegistered) ? contactCode : ThisCustomer.ContactCode.ToDbQuote()) + "))", false);
            }
            else
            {
                warehouseInfo = rateFacade.GetRow(new string[] { "WarehouseCode", "ISOCode", "State", "PostalCode", "AddressType" },
                    "InventoryWarehouse IW (NOLOCK) INNER JOIN SystemCountry SC (NOLOCK) ON IW.Country = SC.CountryCode",
                    "IW.WarehouseCode = (SELECT WarehouseCode FROM CustomerShipToClassTemplateDetailView WHERE ClassCode = (SELECT DefaultRetailCustomerShipToClassTemplate FROM SystemCountry" +
                    " WHERE CountryCode = " + preferredShippingAddress.Country.ToDbQuote() + "))", false);
            }

            if (warehouseInfo != null)
            {
                warehouseCode = warehouseInfo[0];
                isoCode = warehouseInfo[1];
                state = warehouseInfo[2];
                postalCode = warehouseInfo[3];
                IsResidential = (warehouseInfo[4] == "Residential");
            }
            
            if (!shippingMethodCode.IsNullOrEmptyTrimmed() && availableShippingMethods.Count > 0 && (availableShippingMethods[0].FreightCalculation == 1 || availableShippingMethods[0].FreightCalculation == 2))
            {
                decimal freight = decimal.Zero;
                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    
                    using (var reader = DB.GetRSFormat(con, "SELECT Rate FROM EcommerceRealTimeRate with (NOLOCK) WHERE ContactCode = {0} AND ShippingMethodCode = {1}", DB.SQuote(ThisCustomer.ContactCode), DB.SQuote(shippingMethodCode)))
                    {
                        if (reader.Read())
                        {
                            freight = DB.RSFieldDecimal(reader, "Rate");
                        }
                    }
                }
                availableShippingMethods[0].Freight = freight;
            }
            else
            {
                // regular packages and prepacked packages
                CalculateShippingMethodRates(availableShippingMethods, shipments, rateGateway, rateFacade, warehouseCode, isoCode, state, postalCode, IsResidential, getExchangeRate);
                if (!AppLogic.AppConfigBool("ShowShippingMethodError") && !(string.IsNullOrEmpty(AppLogic.AppConfig("ShowShippingMethodError"))))
                {
                    var lstShippingMethodWithoutError = availableShippingMethods.Where(item => item.IsError == false);
                    var newListOfAvailableShippingMethods = new ShippingMethodDTOCollection();
                    newListOfAvailableShippingMethods.AddRange(lstShippingMethodWithoutError);
                    availableShippingMethods = newListOfAvailableShippingMethods;
                }
            }                

            // oversized packages
            var overSizedShipments = GetShipmentComposition(preferredShippingAddress, true);
            foreach (RTShipping.Packages Shipment in overSizedShipments)
            {
                foreach (RTShipping.Package p in Shipment)
                {
                    if (p.OverSizedShippingMethodCode.IsNullOrEmptyTrimmed()) continue;
                        
                    var packageShippingMethod = GetShippingMethod(p.OverSizedShippingMethodCode, warehouseInfo[0]);
                    if (packageShippingMethod == null) continue;

                    // add dummy shipping method
                    if (availableShippingMethods.Count == 0)
                    {
                        using (var con = DB.NewSqlConnection())
                        {
                            con.Open();
                            using (var reader = DB.GetRSFormat(con, string.Format("SELECT ShippingMethod,ShippingMethodDescription FROM CustomerShipTo CST with (NOLOCK) INNER JOIN SystemShippingMethod SSM with (NOLOCK) ON CST.ShippingMethod = SSM.ShippingMethodCode WHERE ShipToCode = {0}", DB.SQuote(ThisCustomer.PrimaryShippingAddressID))))
                            {
                                if (reader.Read())
                                {
                                    var shippingMethod = new ShippingMethodDTO(DB.RSField(reader, "ShippingMethod"), DB.RSField(reader, "ShippingMethodDescription"));
                                    shippingMethod.IsDefault = true;
                                    availableShippingMethods.Add(shippingMethod);
                                }
                            }
                        }
                    }

                    packageShippingMethod.ForOversizedItem = true;
                    packageShippingMethod.OversizedItemName = p.OverSizedItemName;

                    var packageShippingMethodCollection = new ShippingMethodDTOCollection();
                    packageShippingMethodCollection.Add(packageShippingMethod);

                    var tempShipment = new RTShipping.Packages();
                    tempShipment = ShipmentDestination(tempShipment, preferredShippingAddress);

                    var tempShipments = new RTShipping.Shipments();                        
                    tempShipment.AddPackage(p);
                    tempShipments.AddPackages(tempShipment);

                    CalculateShippingMethodRates(packageShippingMethodCollection, tempShipments, rateGateway, rateFacade, warehouseCode, isoCode, state, postalCode, IsResidential, getExchangeRate);
                    availableShippingMethods.Add(packageShippingMethod);                        
                }
            }

            if (rateFacade != null)
            {
                rateFacade.Dispose();
                rateFacade = null;
            }
            if (rateGateway != null)
            {
                rateGateway.Dispose();
                rateGateway = null;
            }

            return availableShippingMethods;
        }

        public ShippingMethodDTOCollection GetShippingMethodsForShippingCalc(Address preferredShippingAddress, string shippingMethodCode)
        {
            var availableShippingMethods = new ShippingMethodDTOCollection();

            if (!IsEmpty())
            {
                RTShipping.Shipments shipments = null;
                shipments = GetShipmentComposition(preferredShippingAddress, false);

                string forCustomer = ThisCustomer.CustomerCode;
                if (ThisCustomer.IsNotRegistered)
                {
                    forCustomer = ThisCustomer.AnonymousCustomerCode;
                }

                decimal getExchangeRate = _facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode);

                if (shipments.Count > 0)
                {
                    using (var con = DB.NewSqlConnection())
                    {
                        con.Open();
                        using (var reader = DB.GetRSFormat(con, "exec eCommerceGetShippingMethodForCalculator @BusinessType = {0}, @CountryCode = {1}, @AddressType = {2}", DB.SQuote(ThisCustomer.DefaultPrice), DB.SQuote(preferredShippingAddress.Country), DB.SQuote(preferredShippingAddress.ResidenceType.ToString())))
                        {
                            while (reader.Read())
                            {
                                var shippingMethod = new ShippingMethodDTO(DB.RSField(reader, "ShippingMethodCode"), DB.RSField(reader, "ShippingMethodDescription"));
                                if (shippingMethod.Code != AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder"))
                                {
                                    shippingMethod.IsDefault = false;
                                    shippingMethod.CarrierCode = DB.RSField(reader, "CarrierCode");
                                    shippingMethod.CarrierDescription = DB.RSField(reader, "CarrierDescription");
                                    shippingMethod.PackagingType = DB.RSField(reader, "PackagingType");
                                    shippingMethod.ServiceType = DB.RSField(reader, "ServiceType");
                                    shippingMethod.FreightCalculation = DB.RSFieldInt(reader, "FreightCalculation");
                                    shippingMethod.ChargeType = DB.RSFieldInt(reader, "ChargeType");
                                    shippingMethod.MiscAmount = DB.RSFieldDecimal(reader, "MiscAmount");
                                    shippingMethod.MiscAmount = _facadeSalesOrder.ConvertCurrency(_facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode), shippingMethod.MiscAmount);
                                    shippingMethod.Length = DB.RSFieldInt(reader, "Length");
                                    shippingMethod.Width = DB.RSFieldInt(reader, "Width");
                                    shippingMethod.Height = DB.RSFieldInt(reader, "Height");
                                    shippingMethod.WeightThreshold = DB.RSFieldDecimal(reader, "WeightThreshold");
                                    availableShippingMethods.Add(shippingMethod);
                                }
                            }
                        }
                    }

                }

                Interprise.Framework.Base.DatasetGateway.Shipping.CarrierDatasetGateway rateGatewayShippingCalculator = null;

                string[] warehouseInfo = null;
                string warehouseCode = string.Empty, isoCode = string.Empty, state = string.Empty, postalCode = string.Empty;
                bool IsResidential = true;

                rateGatewayShippingCalculator = new Interprise.Framework.Base.DatasetGateway.Shipping.CarrierDatasetGateway();
                var rateFacadeShippingCalculator = new Interprise.Facade.Base.Shipping.RateFacade(rateGatewayShippingCalculator);
                // items when shipped are assumed from the Inventory Default/Main Warehouse
                warehouseInfo = rateFacadeShippingCalculator.GetRow(new string[] { "WarehouseCode", "ISOCode", "State", "PostalCode", "AddressType" },
                    "InventoryWarehouse IW (NOLOCK) INNER JOIN SystemCountry SC (NOLOCK) ON IW.Country = SC.CountryCode",
                    "IW.WarehouseCode = (SELECT TOP 1 WarehouseCode FROM CustomerShipTo WHERE ShipToCode IN (SELECT TOP 1 DefaultShippingCode FROM CRMContact WHERE EntityCode = "
                    + DB.SQuote(forCustomer) + " AND ContactCode = " + DB.SQuote(ThisCustomer.ContactCode) + "))", false);
                if (warehouseInfo != null)
                {
                    warehouseCode = warehouseInfo[0];
                    isoCode = warehouseInfo[1];
                    state = warehouseInfo[2];
                    postalCode = warehouseInfo[3];
                    IsResidential = (warehouseInfo[4] == "Residential");
                }
                
                if (!shippingMethodCode.IsNullOrEmptyTrimmed() && availableShippingMethods.Count > 0 && (availableShippingMethods[0].FreightCalculation == 1 || availableShippingMethods[0].FreightCalculation == 2))
                {
                    decimal freight = decimal.Zero;
                    using (var con = DB.NewSqlConnection())
                    {
                        con.Open();
                        
                        using (var reader = DB.GetRSFormat(con, "SELECT Rate FROM EcommerceRealTimeRate with (NOLOCK) WHERE ContactCode = {0} AND ShippingMethodCode = {1}", DB.SQuote(ThisCustomer.ContactCode), DB.SQuote(shippingMethodCode)))
                        {
                            if (reader.Read())
                            {
                                freight = DB.RSFieldDecimal(reader, "Rate");
                            }
                        }
                    }
                    availableShippingMethods[0].Freight = freight;
                }
                else
                {
                    // regular packages and prepacked packages
                    CalculateShippingMethodRates(availableShippingMethods, shipments, rateGatewayShippingCalculator, rateFacadeShippingCalculator, warehouseCode, isoCode, state, postalCode, IsResidential, getExchangeRate);
                    if (!AppLogic.AppConfigBool("ShowShippingMethodError") && !(string.IsNullOrEmpty(AppLogic.AppConfig("ShowShippingMethodError"))))
                    {
                        var lstShippingMethodWithoutError = availableShippingMethods.Where(item => item.IsError == false);
                        var newListOfAvailableShippingMethods = new ShippingMethodDTOCollection();
                        newListOfAvailableShippingMethods.AddRange(lstShippingMethodWithoutError);
                        availableShippingMethods = newListOfAvailableShippingMethods;
                    }
                }

                // oversized packages
                var overSizedShipments = GetShipmentComposition(preferredShippingAddress, true);
                foreach (RTShipping.Packages Shipment in overSizedShipments)
                {
                    foreach (RTShipping.Package p in Shipment)
                    {
                        if (string.IsNullOrEmpty(p.OverSizedShippingMethodCode)) continue;

                        var packageShippingMethod = GetShippingMethod(p.OverSizedShippingMethodCode, warehouseInfo[0]);
                        if (packageShippingMethod == null) continue;

                        // add dummy shipping method
                        if (availableShippingMethods.Count == 0)
                        {
                            using (var con = DB.NewSqlConnection())
                            {
                                con.Open();
                                using (var reader = DB.GetRSFormat(con, string.Format("SELECT ShippingMethod,ShippingMethodDescription FROM CustomerShipTo CST with (NOLOCK) INNER JOIN SystemShippingMethod SSM with (NOLOCK) ON CST.ShippingMethod = SSM.ShippingMethodCode WHERE ShipToCode = {0}", DB.SQuote(ThisCustomer.PrimaryShippingAddressID))))
                                {
                                    if (reader.Read())
                                    {
                                        var shippingMethod = new ShippingMethodDTO(DB.RSField(reader, "ShippingMethod"), DB.RSField(reader, "ShippingMethodDescription"));
                                        shippingMethod.IsDefault = true;
                                        availableShippingMethods.Add(shippingMethod);
                                    }
                                }
                            }
                        }

                        packageShippingMethod.ForOversizedItem = true;
                        packageShippingMethod.OversizedItemName = p.OverSizedItemName;
                        var packageShippingMethodCollection = new ShippingMethodDTOCollection();
                        packageShippingMethodCollection.Add(packageShippingMethod);
                        var tempShipment = new RTShipping.Packages();
                        tempShipment = ShipmentDestination(tempShipment, preferredShippingAddress);
                        var tempShipments = new RTShipping.Shipments();
                        tempShipment.AddPackage(p);
                        tempShipments.AddPackages(tempShipment);
                        CalculateShippingMethodRates(packageShippingMethodCollection, tempShipments, rateGatewayShippingCalculator, rateFacadeShippingCalculator, warehouseCode, isoCode, state, postalCode, IsResidential, getExchangeRate);
                        availableShippingMethods.Add(packageShippingMethod);
                    }
                }

                if (rateFacadeShippingCalculator != null)
                {
                    rateFacadeShippingCalculator.Dispose();
                    rateFacadeShippingCalculator = null;
                }
                if (rateGatewayShippingCalculator != null)
                {
                    rateGatewayShippingCalculator.Dispose();
                    rateGatewayShippingCalculator = null;
                }
            }

            return availableShippingMethods;

        }

        private ShippingMethodDTO GetShippingMethod(string shippingMethodCode, string warehouseCode)
        {
            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, string.Format("SELECT AA.ShippingMethodCode,AA.ShippingMethodDescription,BB.CarrierCode,BB.CarrierDescription,BB.PackagingType,BB.ServiceType,BB.FreightCalculation,BB.ChargeType,BB.MiscAmount " +
                                                                            "FROM SystemShippingMethod AA with (NOLOCK) LEFT JOIN ShipmentShippingMethodDefault BB with (NOLOCK) ON AA.ShippingMethodCode = BB.ShippingMethodCode AND BB.WarehouseCode = {1} WHERE AA.ShippingMethodCode = {0}", DB.SQuote(shippingMethodCode), DB.SQuote(warehouseCode))))
                {
                    if(reader.Read())
                    {
                        var shippingMethod = new ShippingMethodDTO(DB.RSField(reader, "ShippingMethodCode"), DB.RSField(reader, "ShippingMethodDescription"));
                        if (shippingMethod.Code != AppLogic.AppConfig("ShippingMethodCodeZeroDollarOrder"))
                        {
                            shippingMethod.IsDefault = false;
                            shippingMethod.CarrierCode = DB.RSField(reader, "CarrierCode");
                            shippingMethod.CarrierDescription = DB.RSField(reader, "CarrierDescription");
                            shippingMethod.PackagingType = DB.RSField(reader, "PackagingType");
                            shippingMethod.ServiceType = DB.RSField(reader, "ServiceType");
                            shippingMethod.FreightCalculation = DB.RSFieldInt(reader, "FreightCalculation");
                            shippingMethod.ChargeType = DB.RSFieldInt(reader, "ChargeType");
                            shippingMethod.MiscAmount = DB.RSFieldDecimal(reader, "MiscAmount");
                            shippingMethod.MiscAmount = _facadeSalesOrder.ConvertCurrency(_facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode), shippingMethod.MiscAmount);
                        }
                        return shippingMethod;
                    }
                }                
            }
            return null;
        }

        private void CalculateShippingMethodRates(ShippingMethodDTOCollection availableShippingMethods, RTShipping.Shipments shipments, Interprise.Framework.Base.DatasetGateway.Shipping.CarrierDatasetGateway rateGateway,
                Interprise.Facade.Base.Shipping.RateFacade rateFacade, string WarehouseCode, string ISOCode, string State, string PostalCode, bool IsResidential, decimal exchangeRate = 0)
        {            
            decimal cartSubTotal = GetCartSubTotal();
            
            var processedShippingMethods = new ShippingMethodDTOCollection();
            foreach (var shippingMethod in availableShippingMethods)
            {
                if (InterpriseHelper.IsFreeShippingThresholdEnabled(cartSubTotal) &&
                    InterpriseHelper.ShippingMethodCodeBelongsToFreeShippingMethodList(shippingMethod.Description))
                {
                    shippingMethod.Freight = decimal.Zero;
                    shippingMethod.FreightDisplay = AppLogic.GetString("shoppingcart.aspx.13", ThisCustomer.SkinID, ThisCustomer.LocaleSetting);
                    shippingMethod.FreightCurrencyCode = string.Empty;
                }
                else
                {
                    decimal freight = decimal.Zero;                    
                    
                    if (shippingMethod.FreightCalculation == 1 || shippingMethod.FreightCalculation == 2)
                    {
                        // check other shippingMethods with the same carrier AND service AND packaging
                        var similarShippingMethod = GetSimilarShippingMethod(processedShippingMethods, shippingMethod);
                        if (similarShippingMethod != null)
                        {                            
                            freight = similarShippingMethod.ReturnedRate;
                        }
                        else
                        {
                            freight = GetShippingMethodCarrierRate(shippingMethod, shipments, rateFacade, rateGateway, WarehouseCode, ISOCode, State, PostalCode, IsResidential);
                        }                        
                        // implement freight calculation
                        if (shippingMethod.FreightCalculation == 2)
                        {
                            decimal shippingMethodRate = ComputeFreightCharge(shippingMethod.Code, 0, shipments, exchangeRate);
                            switch (shippingMethod.ChargeType)
                            {
                                case 0:
                                    freight += shippingMethodRate;
                                    break;
                                case 1:
                                    if (shippingMethodRate < freight)
                                        freight -= shippingMethodRate;
                                    else
                                        freight = 0;
                                    break;
                            }
                        }
                    }
                    else
                    {
                        freight = ComputeFreightCharge(shippingMethod.Code, shippingMethod.MiscAmount, shipments, exchangeRate);
                    }
                    
                    shippingMethod.Freight = freight;
                    shippingMethod.FreightDisplay = InterpriseHelper.FormatCurrencyForCustomer(freight, ThisCustomer.CurrencyCode);
                    shippingMethod.FreightCurrencyCode = ThisCustomer.CurrencyCode;
                }
                processedShippingMethods.Add(shippingMethod);
            }
        }

        private decimal GetDiscountedAmount(decimal amount)
        {
            if (SalesOrderDataset.CustomerSalesOrderView[0].CouponDiscountType.ToLowerInvariant().Equals("percent"))
            {
                amount = amount - (amount * Decimal.Divide(SalesOrderDataset.CustomerSalesOrderView[0].CouponDiscountPercent, 100));
            }
            else
            {
                amount = amount - SalesOrderDataset.CustomerSalesOrderView[0].CouponDiscountAmount;
            }

            return amount;
        }

        private Decimal ComputeFreightCharge(string shippingMethodCode, decimal miscAmount, RTShipping.Shipments shipments, decimal exchangeRate = 0)
        {
            decimal returnValue = 0;

            string currencyCode = ThisCustomer.CurrencyCode;
            exchangeRate = (exchangeRate == 0) ? _facadeSalesOrder.GetExchangerate(currencyCode) : exchangeRate;

            foreach (RTShipping.Packages Shipment in shipments)
            {
                foreach (RTShipping.Package p in Shipment)
                {
                    decimal tempReturn = 0;
                    decimal baseInsuredValue = _facadeSalesOrder.ConvertCurrency(exchangeRate, p.InsuredValue, true, currencyCode);

                    if (HasCoupon())
                    {
                        SalesOrderFacade.ComputeFreightCharge(shippingMethodCode, GetDiscountedAmount(baseInsuredValue), p.Weight, ref tempReturn, true);
                    }
                    else
                    {
                        SalesOrderFacade.ComputeFreightCharge(shippingMethodCode, baseInsuredValue, p.Weight, ref tempReturn, true);
                    }
                    returnValue += CommonLogic.IIF(p.IsShipSeparately, tempReturn * p.Quantity, tempReturn);
                    returnValue = _facadeSalesOrder.ConvertCurrency(exchangeRate, returnValue);
                    returnValue += CommonLogic.IIF(p.IsShipSeparately, miscAmount * p.Quantity, miscAmount);
                }
            }

            return returnValue;
        }

        private ShippingMethodDTO GetSimilarShippingMethod(ShippingMethodDTOCollection processedShippingMethods, ShippingMethodDTO currentShippingMethod)
        {            
            return processedShippingMethods.FirstOrDefault(shippingMethod => shippingMethod.Code != currentShippingMethod.Code && (shippingMethod.FreightCalculation == 1 || shippingMethod.FreightCalculation == 2) && shippingMethod.CarrierCode == currentShippingMethod.CarrierCode && shippingMethod.ServiceType.ToUpper() == currentShippingMethod.ServiceType.ToUpper() &&
                                                    shippingMethod.PackagingType.ToUpper() == currentShippingMethod.PackagingType.ToUpper());
        }

        private RTShipping.Package CreatePackageComposition(int id, decimal weight, decimal insurance, Dimension dimensions)
        {
            var package = new RTShipping.Package();
            package.PackageId = id;
            package.Length = dimensions.Length;
            package.Width = dimensions.Width;
            package.Height = dimensions.Height;            
            package.Weight = weight;            
            package.InsuredValue = insurance;
            package.Insured = true;
            return package;
        }

        private RTShipping.Shipments GetShipmentComposition(Address shippingAddress, bool isForOverSized)
        {
            // create shipments collection
            var shipments = new RTShipping.Shipments();

            int packageID = 1;
            if (!isForOverSized)
            {
                var itemsThatPrepacked = CartItems.FindAll(new FindAllPrePackedItems().Do);
                foreach (CartItem shipSeparatelyItem in itemsThatPrepacked)
                {
                    var shipment = new RTShipping.Packages();
                    // set the destination address of this shipment
                    shipment = ShipmentDestination(shipment, shippingAddress);
                    // set the origin of this shipment based on the supplier
                    //shipment = ShipmentOrigin(shipment, shipSeparatelyItem.SupplierCode);
                    var package = CreatePackageComposition(packageID++, shipSeparatelyItem.Weight, shipSeparatelyItem.UnitPrice, Dimension.Parse(shipSeparatelyItem.Dimensions));
                    package.IsPrePacked = shipSeparatelyItem.IsPrePacked;
                    package.Quantity = shipSeparatelyItem.m_Quantity;
                    // add packages to the collection
                    shipment.AddPackage(package);
                    shipments.AddPackages(shipment);
                }

                var itemsThatDoesNotShipSeparately = CartItems.FindAll(new FindAllNonShipSeparatelyItems().Do);
                if (itemsThatDoesNotShipSeparately.Count > 0)
                {
                    decimal overallWeight = decimal.Zero;
                    decimal overallInsurance = decimal.Zero;

                    foreach (CartItem nonShipSeparatelyItem in itemsThatDoesNotShipSeparately)
                    {
                        decimal weight = nonShipSeparatelyItem.Weight;

                        //Thread probem - nonShipSeparatelyItem.ThisCustomer - calls http context
                        if (nonShipSeparatelyItem.ThisCustomer == null)
                        {
                            nonShipSeparatelyItem.ThisCustomer = ThisCustomer;
                        }

                        overallWeight += (weight * nonShipSeparatelyItem.m_Quantity);
                        overallInsurance += nonShipSeparatelyItem.Price;
                    }

                    var shipment = new RTShipping.Packages();

                    // set the destination address of this shipment group
                    shipment = ShipmentDestination(shipment, shippingAddress);

                    // Create package object for this item
                    var package = CreatePackageComposition(packageID++, overallWeight, overallInsurance, Dimension.None);

                    shipment.AddPackage(package);
                    shipments.AddPackages(shipment);
                }
            }
            else
            {
                var itemsThatOverSized = CartItems.FindAll(new FindAllOverSizedItems().Do);
                foreach (var shipSeparatelyItem in itemsThatOverSized)
                {
                    var shipment = new RTShipping.Packages();
                    // set the destination address of this shipment
                    shipment = ShipmentDestination(shipment, shippingAddress);
                    // set the origin of this shipment based on the supplier
                    //shipment = ShipmentOrigin(shipment, shipSeparatelyItem.SupplierCode);
                    var package = CreatePackageComposition(packageID++, shipSeparatelyItem.Weight, shipSeparatelyItem.UnitPrice, Dimension.Parse(shipSeparatelyItem.Dimensions));
                    package.IsOverSized = shipSeparatelyItem.IsOverSized;                    
                    package.Quantity = shipSeparatelyItem.m_Quantity;
                    package.OverSizedItemName = shipSeparatelyItem.DisplayName;
                    if (package.IsOverSized)
                    {
                        package.OverSizedShippingMethodCode = shipSeparatelyItem.OverSizedShippingMethodCode;
                    }
                    // add packages to the collection
                    shipment.AddPackage(package);
                    shipments.AddPackages(shipment);
                }
            }

            return shipments;
        }

        private decimal GetShippingMethodCarrierRate(ShippingMethodDTO shippingMethod, RTShipping.Shipments shipments, Interprise.Facade.Base.Shipping.RateFacade rateFacade,
                                                        Interprise.Framework.Base.DatasetGateway.Shipping.CarrierDatasetGateway rateGateway, string warehouseCode, string srcCountry, string srcState, string srcZIP, bool srcIsResidential)
        {
            decimal carrierRate = 0;
            string response = string.Empty;            

            foreach (RTShipping.Packages Shipment in shipments)
            {
                foreach (RTShipping.Package p in Shipment)
                {
                    if (p.IsFreeShipping) { continue; }

                    int lastCount = rateGateway.RateServiceDetail.Rows.Count;

                    if (!p.IsPrePacked && !p.IsOverSized)
                    {
                        try
                        {
                            shippingMethod.IsError = !rateFacade.GetRealTimeRates(ref response, shippingMethod.CarrierCode, shippingMethod.Code, shippingMethod.FreightCalculation, warehouseCode, srcCountry, srcState, srcZIP, srcIsResidential, Shipment.DestinationCountryCode, Shipment.DestinationStateProvince,
                                                        Shipment.DestinationZipPostalCode, Shipment.DestinationIsResidential, p.Weight.ToString(), Decimal.Zero.ToString(), Convert.ToInt32(shippingMethod.Length), Convert.ToInt32(shippingMethod.Width), Convert.ToInt32(shippingMethod.Height),
                                                        shippingMethod.ServiceType, shippingMethod.PackagingType, Customer.Current.CurrencyCode, p.InsuredValue, shippingMethod.WeightThreshold);
                        }
                        catch (Exception ex)
                        {
                            shippingMethod.IsError = true;
                            response = ex.Message;
                        }
                    }
                    else
                    {
                        try
                        {
                            shippingMethod.IsError = !rateFacade.GetRealTimeRates(ref response, shippingMethod.CarrierCode, shippingMethod.Code, shippingMethod.FreightCalculation, warehouseCode, srcCountry, srcState, srcZIP, srcIsResidential, Shipment.DestinationCountryCode, Shipment.DestinationStateProvince,
                                                        Shipment.DestinationZipPostalCode, Shipment.DestinationIsResidential, p.Weight.ToString(), Decimal.Zero.ToString(), Convert.ToInt32(p.Length), Convert.ToInt32(p.Width), Convert.ToInt32(p.Height),
                                                        shippingMethod.ServiceType, shippingMethod.PackagingType, Customer.Current.CurrencyCode, p.InsuredValue, shippingMethod.WeightThreshold);
                        }
                        catch (Exception ex)
                        {
                            shippingMethod.IsError = true;
                            response = ex.Message;
                        }
                    }

                    if (shippingMethod.IsError)
                    {
                        shippingMethod.Description = string.Format("{0} ({1})", shippingMethod.Description, response);                        
                        return 0;
                    }

                    decimal totalRate = decimal.Zero;
                    foreach (DataRow row in rateGateway.RateServiceDetail.Select(string.Format("{0} = '{1}'", rateGateway.RateServiceDetail.ServiceTypeColumn.ColumnName, shippingMethod.ServiceType)))
                    {
                        totalRate += Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(row[rateGateway.RateServiceDetail.TotalColumn.ColumnName], decimal.Zero));
                    }

                    carrierRate += CommonLogic.IIF(p.IsShipSeparately, Convert.ToDecimal(totalRate) * p.Quantity, Convert.ToDecimal(totalRate));
                }
            }
            shippingMethod.ReturnedRate = carrierRate;
            carrierRate += shippingMethod.MiscAmount * shipments.PackageCount;
            return carrierRate;
        }

        public RTShipping.Packages ShipmentDestination(RTShipping.Packages thisShipment, Address destinationAddress)
        {
            thisShipment = new RTShipping.Packages();
            thisShipment.DestinationStateProvince = destinationAddress.State;
            thisShipment.DestinationZipPostalCode = destinationAddress.PostalCode;
            if (!destinationAddress.CountryISOCode.IsNullOrEmptyTrimmed())
            {
                thisShipment.DestinationCountryCode = destinationAddress.CountryISOCode;
            }
            else
            {
                thisShipment.DestinationCountryCode = AppLogic.GetCountryTwoLetterISOCode(destinationAddress.Country);
            }
            thisShipment.DestinationIsResidential = CommonLogic.IIF(destinationAddress.ResidenceType == ResidenceTypes.Residential, true, false);
            return thisShipment;
        }

        private class FindAllShippableItems
        {
            public bool ItemIsShippable(CartItem item)
            {
                return !item.IsDownload && !item.IsService;
            }
        }

        private class FindAllItemsWithSupplierThatShipSeparately : FindAllShippableItems
        {
            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && item.HasSupplier && item.CanShipSeparately && item.IsDropShip;
            }
        }

        private class FindAllItemsWithoutSupplierThatShipSeparately : FindAllShippableItems
        {
            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && !item.HasSupplier && item.CanShipSeparately;
            }
        }

        private class FindAllItemsBySupplierThatDoNotShipSeparately : FindAllShippableItems
        {
            private string _supplierCode = string.Empty;

            public FindAllItemsBySupplierThatDoNotShipSeparately(string supplierCode)
            {
                _supplierCode = supplierCode;
            }

            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && _supplierCode == item.SupplierCode && !item.CanShipSeparately && item.IsDropShip;
            }
        }

        private class FindAllItemsWithoutSupplierThatDoNotShipSeparately : FindAllShippableItems
        {
            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && !item.HasSupplier && !item.CanShipSeparately;
            }
        }

        private class FindAllShipSeparatelyItems 
        {
            public bool Do(CartItem item)
            {
                return item.CanShipSeparately;
            }
        }

        private class FindAllPrePackedItems : FindAllShippableItems
        {
            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && item.IsPrePacked && !item.IsOverSized;
            }
        }

        private class FindAllOverSizedItems : FindAllShippableItems
        {
            public bool Do(CartItem item)
            {
                return ItemIsShippable(item) && item.IsOverSized;
            }
        }

        private class FindAllNonShipSeparatelyItems : FindAllShippableItems
        {        
            public bool Do(CartItem item)
            {
                return !item.CanShipSeparately;                
            }
        }

        public sealed class Dimension
        {
            public static readonly Dimension None = new Dimension();

            private Dimension()
            {
            }

            public Dimension(decimal length, decimal width, decimal height)
            {
                this.Length = length;
                this.Width = width;
                this.Height = height;
            }

            public static Dimension Parse(string s)
            {
                string dimensions = s.ToLowerInvariant();

                if (dimensions.Length != 0)
                {
                    string[] dd = dimensions.Split('x');

                    decimal length, width, height;
                    length = width = height = decimal.Zero;

                    try
                    {
                        length = Localization.ParseUSDecimal(dd[0].Trim());
                    }
                    catch
                    {
                    }
                    try
                    {
                        width = Localization.ParseUSDecimal(dd[1].Trim());
                    }
                    catch
                    {
                    }
                    try
                    {
                        height = Localization.ParseUSDecimal(dd[2].Trim());
                    }
                    catch
                    {
                    }

                    return new Dimension(length, width, height);
                }

                return Dimension.None;
            }

            public decimal Length;
            public decimal Width;
            public decimal Height;
        }        

        public void ClearLineItems()
        {
            foreach (CartItem item in this.CartItems)
            {
                DB.ExecuteSQL("DELETE EcommerceShoppingCart WHERE ShoppingCartRecGuid = '{0}'", item.Id.ToString());
            }

            //Remove item reservation information
            DB.ExecuteSQL("DELETE EcommerceShoppingCartReservation WHERE ContactCode = {0}", DB.SQuote(ThisCustomer.ContactCode));
        }

        public void ClearTransaction()
        {
            ClearLineItems();

            /*****************************************************
            *  5. Clear the Customer coupon info and notes if any
            * ***************************************************/
            string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.CustomerID);
            if (AppLogic.AppConfigBool("ClearCouponAfterOrdering"))
            {
               InterpriseHelper.ClearCustomerCoupon(customerCode, ThisCustomer.IsRegistered);
            }

            DB.ExecuteSQL("UPDATE Customer SET Notes = NULL WHERE CustomerCode = {0}", DB.SQuote(customerCode));


            /***********************************************************
            *  6. Clear the Real-Time rates for this customer if any...
            * **********************************************************/
            DB.ExecuteSQL("DELETE EcommerceRealTimeRate WHERE ContactCode = {0}", DB.SQuote(ThisCustomer.ContactCode));
            ThisCustomer.ClearTransactions(true);
        }

        public decimal GetPackageWeightTotal()
        {
            decimal sum = decimal.Zero;
            // NOTE : Interprise will not support items with separate shipments..
            foreach (CartItem item in CartItems)
            {
                if (item.ItemType != Interprise.Framework.Base.Shared.Const.ITEM_TYPE_ELECTRONIC_DOWNLOAD &&
                    item.ItemType != Interprise.Framework.Base.Shared.Const.ITEM_TYPE_SERVICE)
                {
                    decimal weight = InterpriseHelper.GetItemWeight(item.ItemCode, item.UnitMeasureCode);
                    sum += (item.m_Quantity * weight);
                }
            }

            if (sum == decimal.Zero)
            {
                sum = 0.5M; // must have something to use!
            }

            return sum;
        }

        public bool MeetsMinimumOrderWeight(decimal weight)
        {
            if (weight <= 0 || IsEmpty())
            {
                return true; // disable checking for empty cart or all system products cart (those are handled separately)
            }
            decimal weightTotal = GetPackageWeightTotal();
            return weightTotal >= weight;
        }

        public decimal GetCartSubTotal()
        {
            decimal total = decimal.Zero;

            if (this.IsSalesOrderDetailBuilt &&
                _gatewaySalesOrderDataset != null && !_facadeSalesOrder.ApplyCoupon)
            {
                // get it from the header...
                total = _gatewaySalesOrderDataset.CustomerSalesOrderView[0].SubTotalRate;
            }
            else
            {
                total = CartItems.Sum(item => item.Price);
            }

            return total;
        }

        public decimal GetCartTaxTotal()
        {
            decimal total = decimal.Zero;

            if (this.IsSalesOrderDetailBuilt &&
                _gatewaySalesOrderDataset != null)
            {
                // get it from the header...
                total = _gatewaySalesOrderDataset.CustomerSalesOrderView[0].TaxRate;
            }
            else
            {
                total = CartItems.Sum(item => item.TaxRate);
            }

            return total;
        }

        //added by m.d
        public decimal GetCartSubTotalByGroup(CartItem.CartComparableArgs args, string groupEntityName)
        {
            decimal total = decimal.Zero;
            if (args == CartItem.CartComparableArgs.ITEM_CODE)
            {
                total = CartItems.Where(item => item.ItemCode.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_DESCRIPTION)
            {
                total = CartItems.Where(item => item.ItemDescription.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_NAME)
            {
                total = CartItems.Where(item => item.ItemName.ToLower() == groupEntityName.ToLower())
                                    .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_SUPPLIER_CODE)
            {
                total = CartItems.Where(item => item.ItemSupplierCode.ToLower() == groupEntityName.ToLower())
                                    .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_SUPPLIER_NAME)
            {
                total = CartItems.Where(item => item.ItemSupplierName.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.SUPPLIER_CODE)
            {
                total = CartItems.Where(item => item.SupplierCode.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            return total;        
        }

        public decimal GetCartTaxTotalByGroup(CartItem.CartComparableArgs args, string groupEntityName)
        {
            decimal total = decimal.Zero;
            if (args == CartItem.CartComparableArgs.ITEM_CODE)
            {
                total = CartItems.Where(item => item.ItemCode.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_DESCRIPTION)
            {
                total = CartItems.Where(item => item.ItemDescription.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_NAME)
            {
                total = CartItems.Where(item => item.ItemName.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_SUPPLIER_CODE)
            {
                total = CartItems.Where(item => item.ItemSupplierCode.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.ITEM_SUPPLIER_NAME)
            {
                total = CartItems.Where(item => item.ItemSupplierName.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            else if (args == CartItem.CartComparableArgs.SUPPLIER_CODE)
            {
                total = CartItems.Where(item => item.SupplierCode.ToLower() == groupEntityName.ToLower())
                                 .Sum(item => item.Price);
            }
            return total;
        }
        //end of added code

        public bool MeetsMinimumOrderAmount(decimal amount)
        {
            if (this.IsEmpty()) { return true; }

            decimal subTotal = GetCartSubTotal();
            //  NOTE:
            //  Unless the current setting is Vat.Enabled and our customer's vat setting is Vat.Inclusive (since that line item price is already including the tax)
            //  We will always compute the amount + tax
            bool excludeTax = (AppLogic.AppConfigBool("Vat.Enabled") && ThisCustomer.VATSettingReconciled == VatDefaultSetting.Inclusive);
            if (!excludeTax || this.IsSalesOrderDetailBuilt)
            {
                decimal taxTotal = GetCartTaxTotal();
                subTotal += taxTotal;
            }

            return subTotal >= amount;
        }

        public override bool MeetsMinimumOrderQuantity(Decimal MinOrderQuantity)
        {
            if (MinOrderQuantity <= 0 || IsEmpty())
            {
                return true; // disable checking for empty cart or all system products cart (those are handled separately)
            }

            decimal N = this.CartItems.Sum(c => c.m_Quantity);
            return (N >= MinOrderQuantity);
        }

        public static bool IsCouponHasFreeShipping(string couponCode)
        {
            bool isFreeShipping = false;

            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var dr = DB.GetRSFormat(con, "SELECT DiscountIncludesFreeShipping FROM CustomerSalesCoupon with (NOLOCK) WHERE CouponCode = {0}", DB.SQuote(couponCode)))
                {
                    if (dr.Read())
                    {
                        isFreeShipping = DB.RSFieldBool(dr, "DiscountIncludesFreeShipping");
                    }
                }
            }

            return isFreeShipping;
        }

        public decimal GetOrderTotal()
        {
            if (!IsSalesOrderDetailBuilt)
            {
                throw new InvalidOperationException("Sales Order Detail not yet built!");
            }

            return _gatewaySalesOrderDataset.CustomerSalesOrderView[0].TotalRate;
        }

        public decimal GetOrderBalance()
        {
            if (!IsSalesOrderDetailBuilt)
            {
                throw new InvalidOperationException("Sales Order Detail not yet built!");
            }

            return _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BalanceRate;
        }

        public decimal GetOrderTotal(string orderNumber)
        {
            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var rs = DB.GetRSFormat(con, "select TotalRate from CustomerSalesOrder with (NOLOCK) where BillToCode=" + DB.SQuote(ThisCustomer.CustomerID) + " and SalesOrderCode =" + DB.SQuote(orderNumber)))
                {
                    if (rs.Read())
                    {
                        return DB.RSFieldDecimal(rs, "TotalRate");
                    }
                }
            }
            return 0;
        }

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return CartItems.GetEnumerator();
        }

        #endregion

        public bool MaxMindCheck(string salesOrderCode, Address billingAddress, Address shippingAddress)
        {
            bool fraudCheckPassed = true; // by default so that maxmind fraud failing will not stop the sales order

            if (AppLogic.AppConfigBool("MaxMind.Enabled"))
            {
                decimal fraudScore = -1.0M;
                string fraudDetails = string.Empty;

                try
                {
                    string BillingEMailDomain = billingAddress.EMail.ToLowerInvariant().Trim();
                    if (BillingEMailDomain.Length == 0)
                    {
                        BillingEMailDomain = ThisCustomer.EMail.ToLowerInvariant().Trim();
                    }
                    int i = BillingEMailDomain.IndexOf("@");
                    if (i != -1)
                    {
                        try
                        {
                            BillingEMailDomain = BillingEMailDomain.Substring(i + 1);
                        }
                        catch { }
                    }

                    string TXNID = salesOrderCode;
                    string SessionID = "InterpriseSuiteEcommerce"; // MaxMind requires this value to identify our cart, do not change
                    string EMailMD5 = CommonLogic.IIF(billingAddress.EMail.Length != 0, Security.GetMD5Hash(billingAddress.EMail.Trim().ToLowerInvariant()), Security.GetMD5Hash(ThisCustomer.EMail.Trim().ToLowerInvariant()));
                    string PasswordMD5 = string.Empty; // we are already hashed, there is no way to provided this to this API call
                    string UsernameMD5 = Security.GetMD5Hash(billingAddress.CardName.Trim().ToLowerInvariant());
                    string RequestedType = string.Empty; // use highest level service possible
                    string binName = string.Empty; // we do not collect this
                    string binPhone = string.Empty; // we do not collect this
                    string ForwardedIP = CommonLogic.ServerVariables("HTTP_X_FORWARDED_FOR");
                    string ThisIP = ThisCustomer.LastIPAddress;
                    string RequestIP = CommonLogic.ServerVariables("REMOTE_ADDR");

                    if (ThisIP.Length == 0)
                    {
                        ThisIP = RequestIP;
                    }

                    var mmind = new MaxMindAPI.minfraudWebService();
                    mmind.Url = AppLogic.AppConfig("MaxMind.SOAPURL");

                    string CN = string.Empty;
                    if (billingAddress.CardNumber.Length > 6)
                    {
                        CN = billingAddress.CardNumber.Substring(0, 6);
                    }

                    var rsp = mmind.minfraud_soap(ThisIP,
                                                billingAddress.City,
                                                billingAddress.State,
                                                billingAddress.PostalCode,
                                                billingAddress.Country,
                                                BillingEMailDomain,
                                                CN,
                                                binName,
                                                binPhone,
                                                billingAddress.Phone,
                                                AppLogic.AppConfig("MaxMind.LicenseKey"),
                                                RequestedType,
                                                ForwardedIP,
                                                EMailMD5,
                                                UsernameMD5,
                                                PasswordMD5,
                                                shippingAddress.Address1,
                                                shippingAddress.City,
                                                shippingAddress.State,
                                                shippingAddress.PostalCode,
                                                shippingAddress.Country,
                                                TXNID,
                                                SessionID);

                    fraudDetails = InterpriseHelper.SerializeMaxMindResponse(rsp);
                    // clean up the output a bit:
                    fraudDetails = fraudDetails.Replace(" xmlns=\"http://www.maxmind.com/maxmind_soap/minfraud_soap\"", "");
                    fraudScore = Localization.ParseUSDecimal(rsp.score);

                    // ok, call worked, now let's determine what to do with it: 
                    fraudCheckPassed = fraudScore >= AppLogic.AppConfigUSDecimal("MaxMind.FailScoreThreshold");
                }
                catch (Exception ex)
                {
                    fraudDetails = ex.Message;
                    fraudScore = -1.0M;

                    // don't let maxmind exception stop the order
                    fraudCheckPassed = true;
                }

                var transactionFailed = FailedTransaction.New();
                transactionFailed.CustomerCode = ThisCustomer.CustomerCode;
                transactionFailed.SalesOrderCode = salesOrderCode;
                transactionFailed.PaymentGateway = "MaxMind";
                transactionFailed.MaxMindDetails = fraudDetails;
                transactionFailed.TransactionCommand = FailedTransaction.NOT_APPLICABLE;
                transactionFailed.TransactionResult = fraudDetails;
                transactionFailed.IPAddress = ThisCustomer.LastIPAddress;
                transactionFailed.MaxMindFraudScore = fraudScore;
                transactionFailed.IsFailed = !fraudCheckPassed;

                // save the failed transaction
                transactionFailed.Record();
            }

            return fraudCheckPassed;
        }

        private void AddLineItem(CartItem item)
        {
            using (var facadeList = new ListControlFacade())
            {
                using (var datasetItem = new DataSet())
                {
                    var itemBaseDataset = new BaseDataset();

                    //var umInfo = InterpriseHelper.GetItemUnitMeasure(item.ItemCode, item.UnitMeasureCode);
                    string query = string.Format("ItemCode = {0} AND (WarehouseCode = {1} or WarehouseCode IS NULL) AND UnitMeasureCode = {2}",
                                    DB.SQuote(item.ItemCode),
                                    DB.SQuote(ThisCustomer.WarehouseCode),
                                    DB.SQuote(item.UnitMeasureCode));

                    //deadlock
                    int nooftries = 0;
                    while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
                    {
                        try
                        {
                            facadeList.ReadSearchResultsData("SaleItemView", query, 1, false, string.Empty, ref itemBaseDataset, true);
                            break;
                        }
                        catch (SqlException ex)
                        {
                            if (ex.ErrorCode == 1205)
                            {
                                nooftries += 1;
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }

                    if (itemBaseDataset.Tables["SaleItemView"] != null &&
                        itemBaseDataset.Tables["SaleItemView"].Rows.Count > 0)
                    {
                        var itemRow = itemBaseDataset.Tables["SaleItemView"].Rows[0];
                        var dvSalesOrderItem = new DataView(_gatewaySalesOrderDataset.CustomerSalesOrderDetailView);
                        var lineItemRow = dvSalesOrderItem.AddNew();

                        switch ((string)itemRow["ItemType"])
                        {
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_STOCK:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_NON_STOCK:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_MATRIX_GROUP:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_MATRIX_ITEM:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_SERVICE:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_ELECTRONIC_DOWNLOAD:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_ASSEMBLY:
                                string error = string.Empty;
                                if (_facadeSalesOrder.AssignInventoryItem(itemRow, ref lineItemRow, ref error))
                                {
                                    lineItemRow["QuantityOrdered"] = item.m_Quantity;
                                    _facadeSalesOrder.AssignQuantityOrdered(lineItemRow, _facadeSalesOrder.TransactionType);
                                    bool byTotalQty = false;
                                    _facadeSalesOrder.SetSalesPrice(lineItemRow, ref byTotalQty);
                                    _facadeSalesOrder.Compute(lineItemRow.Row, _facadeSalesOrder.TransactionType);
                                }
                                break;

                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT:
                                lineItemRow["QuantityOrdered"] = item.m_Quantity;
                                AssignKitItem(itemRow, lineItemRow, item.Id);

                                // NOTE:
                                //  call this function so that the quantity per kit would recompute
                                //  therefore have the correct computation for other unit measures like dozen
                                //  which takes into consideration the unit measure quantity
                                _facadeSalesOrder.AssignQuantityOrdered(lineItemRow, _facadeSalesOrder.TransactionType);
                                break;
                        }

                        lineItemRow["WebSiteCode"] = InterpriseHelper.ConfigInstance.WebSiteCode;
                        // NOTE:
                        //  We only use text option for OrderOption items that has a textbox provided so they can enter their custom notes....
                        //  So we only assume here 2 things if it's not empty:
                        //      1. It was previously an Order option
                        //      2. A note has been specified by the customer
                        string itemTextOption = item.m_TextOption;
                        if (!string.IsNullOrEmpty(itemTextOption))
                        {
                            lineItemRow["ItemDescription"] = itemTextOption;
                        }

                        item.AssociatedLineItemRow = lineItemRow.Row as SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow;
                    }
                    // Explicitly dereference here
                    itemBaseDataset.Dispose();
                    itemBaseDataset = null;
                }
            }
        }

        private void AddLineItem(string itemCode, string UMCode, DataRow whRow, decimal qtyOrdered, string itemDescription, Guid itemGUID)
        {
            using (var facadeList = new ListControlFacade())
            {
                using (var datasetItem = new DataSet())
                {
                    var itemBaseDataset = new BaseDataset();

                    var umInfo = InterpriseHelper.GetItemUnitMeasure(itemCode, UMCode);
                    string query = string.Format("ItemCode = {0} AND (WarehouseCode = {1} or WarehouseCode IS NULL) AND UnitMeasureCode = {2}",
                                    DB.SQuote(itemCode),
                                    DB.SQuote(whRow["WarehouseCode"].ToString()),
                                    DB.SQuote(umInfo.Code));

                    //deadlock
                    int nooftries = 0;
                    while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
                    {
                        try
                        {
                            facadeList.ReadSearchResultsData("SaleItemView", query, 1, false, string.Empty, ref itemBaseDataset, true);
                            break;
                        }
                        catch (SqlException ex)
                        {
                            if (ex.ErrorCode == 1205)
                            {
                                nooftries += 1;
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }

                    if (itemBaseDataset.Tables["SaleItemView"] != null &&
                        itemBaseDataset.Tables["SaleItemView"].Rows.Count > 0)
                    {
                        var itemRow = itemBaseDataset.Tables["SaleItemView"].Rows[0];
                        var dvSalesOrderItem = new DataView(_gatewaySalesOrderDataset.CustomerSalesOrderDetailView);
                        var lineItemRow = dvSalesOrderItem.AddNew();

                        switch ((string)itemRow["ItemType"])
                        {
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_STOCK:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_NON_STOCK:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_MATRIX_GROUP:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_MATRIX_ITEM:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_SERVICE:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_ELECTRONIC_DOWNLOAD:
                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_ASSEMBLY:
                                string error = string.Empty;
                                if (_facadeSalesOrder.AssignInventoryItem(itemRow, ref lineItemRow, ref error))
                                {
                                    lineItemRow["QuantityOrdered"] = qtyOrdered;                                    
                                    _facadeSalesOrder.AssignQuantityOrdered(lineItemRow, _facadeSalesOrder.TransactionType);                                    
                                    _facadeSalesOrder.AssignLineItemWarehouse(lineItemRow, whRow);
                                    bool byTotalQty = false;
                                    _facadeSalesOrder.SetSalesPrice(lineItemRow, ref byTotalQty);
                                    _facadeSalesOrder.Compute(lineItemRow.Row, _facadeSalesOrder.TransactionType);
                                }
                                break;

                            case Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT:
                                lineItemRow["QuantityOrdered"] = qtyOrdered;                                
                                AssignKitItem(itemRow, lineItemRow, itemGUID);
                                _facadeSalesOrder.AssignLineItemWarehouse(lineItemRow, whRow);                                

                                // NOTE:
                                //  call this function so that the quantity per kit would recompute
                                //  therefore have the correct computation for other unit measures like dozen
                                //  which takes into consideration the unit measure quantity
                                _facadeSalesOrder.AssignQuantityOrdered(lineItemRow, _facadeSalesOrder.TransactionType);
                                break;
                        }

                        lineItemRow["WebSiteCode"] = InterpriseHelper.ConfigInstance.WebSiteCode;
                        // NOTE:
                        //  We only use text option for OrderOption items that has a textbox provided so they can enter their custom notes....
                        //  So we only assume here 2 things if it's not empty:
                        //      1. It was previously an Order option
                        //      2. A note has been specified by the customer                        
                        if (!string.IsNullOrEmpty(itemDescription))
                        {
                            lineItemRow["ItemDescription"] = itemDescription;
                        }
                    }
                    // Explicitly dereference here
                    itemBaseDataset.Dispose();
                    itemBaseDataset = null;
                }
            }
        }

        public void BuildSalesOrderDetails(bool computeFreight, bool computeTax, string couponCode = "")
        {
            if (IsEmpty())
            {
                return;
            }


            int retries = 3;

            for (int ctr = 0; ctr < retries; ctr++)
            {
                try
                {
                    _gatewaySalesOrderDataset = new SalesOrderDatasetGateway();
                    _facadeSalesOrder = new SalesOrderFacade(_gatewaySalesOrderDataset);

                    BuildSalesOrderDetailsCore(computeFreight, computeTax, couponCode);
                    break;
                }
                catch
                {
                    _gatewaySalesOrderDataset.Dispose();
                    _facadeSalesOrder.Dispose();

                    _gatewaySalesOrderDataset = null;
                    _facadeSalesOrder = null;

                    // if this is the last retry, bubble up the error
                    if (ctr + 1 == retries)
                    {
                        throw;
                    }
                }
            }
        }

        private void BuildSalesOrderDetailsCore(bool computeFreight, bool computeTax, string couponCode = "")
        {
            //For safety double check if couponCode is not supplied.
            if(couponCode.IsNullOrEmptyTrimmed()) { HasCoupon(ref couponCode); }

            // use Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder to show the reservation value.
            _facadeSalesOrder.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder;
            decimal getExchangeRate = _facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode);

            string shipToCode = CommonLogic.IIF(ThisCustomer.IsNotRegistered, ThisCustomer.AnonymousShipToCode, ThisCustomer.PrimaryShippingAddressID);

            var shipToDataset = new DataSet();
            var facadeListControl = new ListControlFacade();
            var shipToBaseDataset = new BaseDataset();

            //deadlock
            int nooftries = 0;
            while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
            {
                try
                {
                    shipToDataset = facadeListControl.ReadSearchResultsData("CustomerShipToView", 
                                                                            string.Format("ShipToCode = {0}", DB.SQuote(shipToCode)), 
                                                                            1, 
                                                                            false, 
                                                                            string.Empty, 
                                                                            ref shipToBaseDataset, true);
                    break;
                }
                catch (SqlException ex)
                {
                    if (ex.ErrorCode == 1205)
                    {
                        nooftries += 1;
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            var rowShipTo = shipToBaseDataset.Tables["CustomerShipToView"].Rows[0];
            string contactCode = string.Empty;
            string contactFullName = string.Empty;

            if (ThisCustomer.IsRegistered)
            {
                // retrieve the contact info
                contactCode = ThisCustomer.ContactCode;
                contactFullName = ThisCustomer.ContactFullName;
            }
            else
            {
                //since anonymous checkout is enabled, calculate tax based on the shipping address they entered
                var shippingAddress = Address.Get(ThisCustomer, AddressTypes.Shipping, this.FirstItem().m_ShippingAddressID);
                rowShipTo["ShipToName"] = shippingAddress.Name;
                rowShipTo["Address"] = shippingAddress.Address1;
                rowShipTo["City"] = shippingAddress.City;
                rowShipTo["State"] = shippingAddress.State;
                rowShipTo["PostalCode"] = shippingAddress.PostalCode;
                rowShipTo["CountryCode"] = shippingAddress.CountryISOCode;
            }

            /***************************************************************
            *  1. Make Sales Order
            * *************************************************************/
            string msg = string.Empty;            
            if (_facadeSalesOrder.CreateSalesOrder(_facadeSalesOrder.TransactionType,
                                            rowShipTo,
                                            contactCode,
                                            contactFullName,
                                            null,
                                            ref msg,
                                            string.Empty,
                                            string.Empty, null, null,true))
            {

                //for anonymous checkout --- passed the shipping address they entered into customersalesorderview to calculate the tax
                if (ThisCustomer.IsNotRegistered)
                {
                    var shippingAddress = Address.Get(ThisCustomer, AddressTypes.Shipping, this.FirstItem().m_ShippingAddressID);
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShipToName = shippingAddress.Name;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShipToAddress = shippingAddress.Address1;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShipToCity = shippingAddress.City;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShipToState = shippingAddress.State;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ShipToPostalCode = shippingAddress.PostalCode;
                }

                if (ThisCustomer.IsRegistered)
                {
                    // apply the notes...
                    if (!ThisCustomer.ThisCustomerSession["paypalfrom"].IsNullOrEmptyTrimmed())
                    {
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].InternalNotes = ThisCustomer.Notes;
                    }
                    else
                    {
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].PublicNotes = ThisCustomer.Notes;
                    }
                }

                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].WebSiteCode = InterpriseHelper.ConfigInstance.WebSiteCode;

                if (ThisCustomer.IsRegistered)
                {
                    //override default billing address from primary billing address
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToName = ThisCustomer.PrimaryBillingAddress.CardName;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToAddress = ThisCustomer.PrimaryBillingAddress.Address1;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToCity = ThisCustomer.PrimaryBillingAddress.City;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToState = ThisCustomer.PrimaryBillingAddress.State;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToPostalCode = ThisCustomer.PrimaryBillingAddress.PostalCode;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToCounty = ThisCustomer.PrimaryBillingAddress.County;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToCountry = ThisCustomer.PrimaryBillingAddress.Country;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToPhone = ThisCustomer.PrimaryBillingAddress.Phone;
                }

                this.CartItems.ForEach(item => AddLineItem(item));

                // Compute Shipping
                if (!this.HasShippableComponents())
                {
                    this.MakeShippingNotRequired(false);
                }

                bool doesCouponIncludeFreeShipping = CouponIncludesFreeShipping(couponCode);
                ComputeFreightCharge(!computeFreight || doesCouponIncludeFreeShipping, getExchangeRate);

                if (!computeTax)
                {
                    foreach (var lineItemRow in _gatewaySalesOrderDataset.CustomerSalesOrderDetailView)
                    {
                        lineItemRow.Tax = string.Empty;
                        lineItemRow.TaxCode = string.Empty;
                        lineItemRow.SalesTaxAmount = decimal.Zero;
                        lineItemRow.SalesTaxAmountRate = decimal.Zero;
                    }
                    // zero out the tax so it won't alter the computation of the coupon discount
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].FreightTaxCode = string.Empty;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].FreightTaxScheme = string.Empty;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].OtherTaxCode = string.Empty;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].OtherTaxScheme = string.Empty;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].Tax = decimal.Zero;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].TaxRate = decimal.Zero;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].OtherTax = decimal.Zero;
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].OtherTaxRate = decimal.Zero;
                }
                
                if (!computeFreight && !computeTax)
                {
                    _facadeSalesOrder.ComputeTotals(
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0],
                        _gatewaySalesOrderDataset.CustomerSalesOrderDetailView,
                        true,
                        false
                    );
                }

                    // if instantiating this object was caused by a postback on entering coupon code
                    // we must re-apply the coupon discount...

                if (!couponCode.IsNullOrEmptyTrimmed())
                {
                    ApplyCoupon(couponCode);
                }
                else
                {
                    if (HasCoupon(ref couponCode)) ApplyCoupon(couponCode);
                }

                // recompute just to be sure, esp in real time shipping..
                // the freight my be zeroed out once we set a non-existent shipping method
                ComputeFreightCharge(!computeFreight || doesCouponIncludeFreeShipping, getExchangeRate);

                // perform new allocation and reservation procedure
                ProcessAllocationAndReservation();
            }
        }

        private void ComputeShipping()
        {
            // Compute Shipping
            if (!this.HasShippableComponents())
            {
                this.MakeShippingNotRequired(false);
            }
        }

        private void ApplyCoupon()
        {
            /***************************************************************
            *  1.b. Apply the coupon, if any...
            * *************************************************************/
            // NOTE : 
            //  Once we reach here, we assume that the coupon( if any ) is valid
        
                bool hasCoupon = false;
                string couponCode = string.Empty;
                string customerCode = CommonLogic.IIF(ThisCustomer.IsRegistered, ThisCustomer.CustomerCode, ThisCustomer.CustomerID);
                // retrieve the coupon from the customer table
                string couponQuery = string.Format("SELECT CouponCode FROM Customer with (NOLOCK) WHERE CustomerCode = {0}", DB.SQuote(customerCode));

                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, couponQuery))
                    {
                        if (reader.Read())
                        {
                            hasCoupon = reader["CouponCode"] != null &&
                                        reader["CouponCode"] != DBNull.Value &&
                                        !string.IsNullOrEmpty((string)reader["CouponCode"]);

                            if (hasCoupon)
                                couponCode = (string)reader["CouponCode"];
                        }
                    }
                }

                if (hasCoupon)
                {
                    // apply...
                    var couponDataset = new DataSet();
                    var facadeList = new ListControlFacade();
                    var baseCouponDataset = new BaseDataset();

                    //deadlock
                    int nooftries = 0;
                    while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
                    {
                        try
                        {
                            facadeList.ReadSearchResultsData("CustomerCouponView", string.Format("CouponCode = {0}", DB.SQuote(couponCode)), 1, false, string.Empty, ref baseCouponDataset, true);
                            break;
                        }
                        catch (SqlException ex)
                        {
                            if (ex.ErrorCode == 1205)
                            {
                                nooftries += 1;
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }

                    if (baseCouponDataset.Tables["CustomerCouponView"] != null &&
                        baseCouponDataset.Tables["CustomerCouponView"].Rows.Count >= 1)
                    {
                        var couponRow = baseCouponDataset.Tables["CustomerCouponView"].Rows[0];
                        _facadeSalesOrder.AssignCoupon(couponRow, _gatewaySalesOrderDataset.CustomerSalesOrderView[0]);
                    }
                }
        }

        private void TagOrder(string salesOrderCode)
        {
            // mark this order
            _gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode = salesOrderCode;

            if (ThisCustomer.PaymentTermCode.ToUpper() == "PURCHASE ORDER")
            {
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].POCode = CommonLogic.IIF(ThisCustomer.ThisCustomerSession.Session("PONumber") != "", ThisCustomer.ThisCustomerSession.Session("PONumber") + "/Web Order", "Web Order");
                ThisCustomer.ThisCustomerSession.ClearVal("PONumber");
            }
            else
            {
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].POCode = "Web Order";
            }
            _gatewaySalesOrderDataset.CustomerSalesOrderView[0].SourceCode = "Internet";

            if (ThisCustomer.IsRegistered)
            {
                _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ContactCode = ThisCustomer.ContactCode;
            }
        }

        private void AssignAffiliate()
        {
            /***************************************************************
           *  1.c. Assign the affiliate(if any)
           * *************************************************************/
            // Note :
            //  If this is the first affiliate/sales rep used by this customer
            //  We will assign this affiliate to always be used by this customer
            //  So that every order will be tagged for this affiliate.
            //  Otherwise if the customer already has a affiliate set up
            //  and the current affiliate is not the default(Which is the very first affiliate assigned to this customer)
            //  We will tag this affiliate only for this order
            bool isAffiliateDefinedAndValid = false;
            bool affiliateIsCustomerDefault = false;
            bool customerHasDefaultAffiliate = false;

            // NOTE :
            //  Affiliate ID from customer is always retrieved from the cookie
            string affiliateId = ThisCustomer.AffiliateID;

            //  let's just validate if it has an affiliate regardless if the current cached affiliate
            //  is the customer's default or not since if it's a new, tagging for this affiliate
            //  will only occur on this transaction and succeeding transactions will always associate
            //  to the customer default.
            isAffiliateDefinedAndValid = !string.IsNullOrEmpty(affiliateId) && InterpriseSuiteEcommerceCommon.InterpriseIntegration.Affiliate.IsValid(affiliateId);

            customerHasDefaultAffiliate = _gatewaySalesOrderDataset.CustomerSalesRepCommissionView.Count > 0;

            if (customerHasDefaultAffiliate)
            {
                if (!isAffiliateDefinedAndValid)
                {
                    affiliateIsCustomerDefault = true;
                }
                else
                {
                    affiliateIsCustomerDefault = (affiliateId == _gatewaySalesOrderDataset.CustomerSalesRepCommissionView[0].SalesRepGroupCode);
                }
            }
            else if (isAffiliateDefinedAndValid)
            {
                affiliateIsCustomerDefault = false;
            }

            if (isAffiliateDefinedAndValid && !affiliateIsCustomerDefault)
            {
                if (_gatewaySalesOrderDataset.CustomerSalesRepCommissionView.Count > 0)
                {
                    _gatewaySalesOrderDataset.CustomerSalesRepCommissionView.Clear();
                    _facadeSalesOrder.ClearCommission();
                }

                // retrieve the datarow from the database
                using (var facadeSalesRepList = new ListControlFacade())
                {
                    var baseSalesRepDataset = new BaseDataset();
                    var salesRepDataset = new DataSet();

                    facadeSalesRepList.ReadSearchResultsData("CustomerSalesRepView",
                        string.Format("ApplyTo = 'Sales' AND SalesRepGroupCode = {0}", DB.SQuote(affiliateId)),
                        1,
                        false,
                        string.Empty,
                        ref baseSalesRepDataset,
                        true);

                    if (baseSalesRepDataset.Tables["CustomerSalesRepView"] != null &&
                        baseSalesRepDataset.Tables["CustomerSalesRepView"].Rows.Count >= 1)
                    {
                        var salesRepRow = baseSalesRepDataset.Tables["CustomerSalesRepView"].Rows[0];
                        var salesRepCommisionRow = (new DataView(_gatewaySalesOrderDataset.CustomerSalesRepCommissionView)).AddNew();
                        _facadeSalesOrder.AssignSalesRepCommission(salesRepRow, salesRepCommisionRow);

                        // assign the contact...
                        var affliateInfo = InterpriseIntegration.Affiliate.FindById(affiliateId);
                        salesRepCommisionRow.BeginEdit();
                        salesRepCommisionRow["ContactCode"] = affliateInfo.ContactCode;
                        salesRepCommisionRow["ContactFullName"] = affliateInfo.FullName;
                        salesRepCommisionRow.EndEdit();
                    }
                    // Explicitly dereference
                    baseSalesRepDataset.Dispose();
                    salesRepDataset.Dispose();

                    baseSalesRepDataset = null;
                    salesRepDataset = null;
                }
            }

            if (isAffiliateDefinedAndValid && !affiliateIsCustomerDefault)
            {
                //  Now, if the customer doesn't have an affiliate set up
                //  make this first one the default
                DB.ExecuteSQL("UPDATE Customer SET SalesRepGroupCode = {0} WHERE CustomerCode = {1}",
                    DB.SQuote(affiliateId),
                    DB.SQuote(ThisCustomer.CustomerCode));
            }
        }

        private void UpdateBillingAndShippingInformation(Address billingAddress, Address shippingAddress)
        {
            //***************************************************************
            //  Update the Bill To and Ship To Information
            //  Incase the customer has multiple billto's and shipto's
            //***************************************************************
            var salesOrderRow = _gatewaySalesOrderDataset.CustomerSalesOrderView[0];
            // set the payment term based on the customer...
            salesOrderRow.PaymentTermCode = ThisCustomer.PaymentTermCode;
            // billing information                    
            salesOrderRow.BillToName = billingAddress.Name;
            salesOrderRow.BillToAddress = billingAddress.Address1;
            salesOrderRow.BillToCity = billingAddress.City;
            salesOrderRow.BillToCounty = billingAddress.County;
            salesOrderRow.BillToState = billingAddress.State;
            salesOrderRow.BillToPostalCode = billingAddress.PostalCode;
            salesOrderRow.BillToCountry = billingAddress.Country;
            salesOrderRow.BillToPhone = billingAddress.Phone;

            // shipping information
            salesOrderRow.ShipToName = shippingAddress.Name;
            salesOrderRow.ShipToAddress = shippingAddress.Address1;
            salesOrderRow.ShipToCity = shippingAddress.City;
            salesOrderRow.ShipToCounty = shippingAddress.County;
            salesOrderRow.ShipToState = shippingAddress.State;
            salesOrderRow.ShipToPostalCode = shippingAddress.PostalCode;
            salesOrderRow.ShipToCountry = shippingAddress.Country;
            salesOrderRow.ShipToPhone = shippingAddress.Phone;
            if (ThisCustomer.IsNotRegistered)
            {
                salesOrderRow.ShippingMethodGroup = shippingAddress.ShippingMethodGroup;
                salesOrderRow.PaymentTermGroup = shippingAddress.PaymentTermGroup;
            }
        }

        private void SaveSalesOrder()
        {
            //Retry saving if concurrency error was encountered otherwise, simply throw the exception.
            int nooftries = 0;
            while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
            {
                try
                {
                    string[] relatedTables = _facadeSalesOrder.get_RelatedTables(_facadeSalesOrder.TransactionType);
                    string[][] commands = _facadeSalesOrder.CreateParameterSet(relatedTables);

                    //Set this property to skip stock deallocation. This should fix the deadlock issue.
                    var soDataset = (SalesOrderDatasetGateway)_facadeSalesOrder.CurrentDataset;
                    foreach (DataRow drow in soDataset.CustomerSalesOrderDetailView.Rows)
                    {
                        drow.BeginEdit();

                        if (_facadeSalesOrder.TransactionType == Interprise.Framework.Base.Shared.Enum.TransactionType.Quote)
                        {
                            drow[soDataset.CustomerSalesOrderDetailView.SourceDocumentTypeColumn.ColumnName] = Interprise.Framework.Base.Shared.Enum.TransactionType.Quote.ToString();
                        }

                        if (System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0)) > 0)
                        {
                            drow[soDataset.CustomerSalesOrderDetailView.DueDateColumn.ColumnName] = _facadeSalesOrder.LatestShippingDate;
                        }
                        else
                        {
                            drow[soDataset.CustomerSalesOrderDetailView.DueDateColumn.ColumnName] = DateTime.Now;
                        }

                        drow.EndEdit();
                    }

                    //Check if transaction is Quote Then Clear reservation
                    if (_facadeSalesOrder.TransactionType == Interprise.Framework.Base.Shared.Enum.TransactionType.Quote)
                    {
                       //Change Header Type to Quote
                        foreach (DataRow drow in soDataset.CustomerSalesOrderView.Rows)
                        {
                            drow.BeginEdit();
                            drow[soDataset.CustomerSalesOrderView.TypeColumn.ColumnName] = Interprise.Framework.Base.Shared.Enum.TransactionType.Quote.ToString();
                            drow.EndEdit();
                        }
                        
                        _facadeSalesOrder.IsSkipStockDeallocation = false;
                        _facadeSalesOrder.ClearTransactionReservation(true);
                    }
                    else
                    {
                    _facadeSalesOrder.IsSkipStockDeallocation = true;
                    }
                    
                    _facadeSalesOrder.UpdateDataSet(commands, _facadeSalesOrder.TransactionType, string.Empty, false);
                    break;
                }
                catch (DBConcurrencyException)
                {
                    nooftries += 1;
                    SaveSalesOrderAndAllocate(_facadeSalesOrder.CurrentDataset.Tables["CustomerSalesOrderView"].Rows[0]["SalesOrderCode"].ToString());
                }
                catch (Exception)
                {
                    //???
                    throw;
                }
            }
        }

        private void SaveSalesOrderAndAllocate(string salesOrderCode)
        {
            //Only save after allocation/reservation has completed
            if (ProcessAllocationAndReservation()) { SaveSalesOrder(); }
        }
        
        private bool ProcessAllocationAndReservation() 
        { 
            try 
            {
                //get so dataset instance
                var soDataset = (SalesOrderDatasetGateway)_facadeSalesOrder.CurrentDataset;
                
                // Clear Reservation transaction - if sales order code equal to temporary document code
                _facadeSalesOrder.ClearTransactionReservation(soDataset.CustomerSalesOrderView[0].SalesOrderCode.ToString() == Interprise.Framework.Base.Shared.Const.TEMPORARY_DOCUMENTCODE);
                
                //Call for INITIAL allocation of items on the order
                _facadeSalesOrder.AllocateStock();
                //Save allocated qty to ecommerce shopping cart
                StoreAllocatedQuantities(soDataset);

                //if ShowInventoryFromAllWarehouses == true  
                if (System.Convert.ToBoolean(AppLogic.AppConfig("ShowInventoryFromAllWarehouses")))
                {
                    if (soDataset.CustomerSalesOrderView[0].SalesOrderCode.ToString() == Interprise.Framework.Base.Shared.Const.TEMPORARY_DOCUMENTCODE)
                    {   
                        ProcessAllocationAndReservationAllWH();
                    }
                    //else { ReInitializeReservation(soDataset, false); }
                    ReInitializeReservation(soDataset, false);
                }
                else {
                    //Modify shipping date to max date to allow the system to retrieve all POs available for reservation
                    soDataset.CustomerSalesOrderView[0].BeginEdit();
                    soDataset.CustomerSalesOrderView[0][soDataset.CustomerSalesOrderView.ShippingDateColumn.ColumnName] = Interprise.Framework.Base.Shared.Const.MAX_SMALLDATETIME;
                    soDataset.CustomerSalesOrderView[0].EndEdit();
                    
                    //Modify each due date in line item 
                    foreach (DataRow drow in soDataset.CustomerSalesOrderDetailView.Rows)
                    {
                        drow.BeginEdit();
                        drow[soDataset.CustomerSalesOrderDetailView.DueDateColumn.ColumnName]= Interprise.Framework.Base.Shared.Const.MAX_SMALLDATETIME;
                        drow.EndEdit();
                    }

                    //Deletes currently stored reservation information to make way for an updated one
                    //Also modifies the TransactionCode of the reservation table to match that of the current sales order code.
                    ReInitializeReservation(soDataset);

                    foreach (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow in soDataset.CustomerSalesOrderDetailView.Rows)
                    {
                        //begin qty reservation
                        _facadeSalesOrder.LoadItemReservation(drow);
                        _facadeSalesOrder.ReserveStock(drow);
                        //store reservation data
                        if (System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0)) > 0)
                        {
                            if (drow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                            {
                                StoreReservedKitQuantity(drow);
                            }
                            else
                            {
                                StoreReservedQuantity(drow.ItemCode);
                            }
                        }
                    }
                }


                //Modify shipping date
                soDataset.CustomerSalesOrderView[0].BeginEdit();
                if (HasAllocatedQuantity())
                {
                    //Cart has allocated quantities, return shipping date to the current date...
                    soDataset.CustomerSalesOrderView[0][soDataset.CustomerSalesOrderView.ShippingDateColumn.ColumnName] = DateTime.Now;
                }
                else
                {
                    //Remodify ship date to retrieve the latest possible shipping date based on the reservation dates...
                    soDataset.CustomerSalesOrderView[0][soDataset.CustomerSalesOrderView.ShippingDateColumn.ColumnName] = _facadeSalesOrder.LatestShippingDate;
                }
                soDataset.CustomerSalesOrderView[0].EndEdit();
  
                //object disposal/dereference
                soDataset.Dispose();
                soDataset = null;                

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private void ProcessAllocationAndReservationAllWH()
        {
            var soDataset = (SalesOrderDatasetGateway)_facadeSalesOrder.CurrentDataset;

            //retrieve available warehouses
            DataTable dtWarehouse = GetWarehouse();

            //START ALLOCATION CODE ------------------------------------------------------

            //note original cart item count
            int rowCount = this.CartItems.Count;
            decimal qtyToReserve = 0;
            Object[][] reserveInfo = new Object[this.CartItems.Count][];

            //NOTE: we use "for" instead of "foreach" to avoid infinite looping since we are adding new rows to the detail table...
            //check rows if there are line items that have unallocated/unreserved quantities
            for (int rowIndex = 0; rowIndex <= rowCount - 1; rowIndex++)
            {
                var drow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[rowIndex];

                //Get cart item GUID
                var itemGuid = new Guid();
                var cartItem = this.CartItems.FirstOrDefault(item => item.ItemCode == drow.ItemCode);
                if (cartItem != null) { itemGuid = cartItem.Id; }
                
                decimal qtyOrigOrd = drow.QuantityOrdered; //original qty order for the line item
                decimal qtyAlloc = drow.QuantityAllocated; //qty allocated (so far) for the line item

                //filter the list of warehouses available (excluding the default) from the active warehouses
                string[] whFiltered = GetWarehouseFiltered(dtWarehouse, drow.WarehouseCode);

                //allocate
                if (qtyOrigOrd > qtyAlloc)
                {
                    //loop warehouses
                    for (int whCtr = 0; whCtr <= whFiltered.Length - 1; whCtr++)
                    {
                        DataRow[] whRow = dtWarehouse.Select(string.Format("WarehouseCode = '{0}'", whFiltered[whCtr].ToString()));

                        //Add as a new order line item with new warehouse
                        AddLineItem(drow.ItemCode, drow.UnitMeasureCode, whRow[0], qtyOrigOrd - drow.QuantityAllocated, drow.ItemDescription, itemGuid);
                        var newRow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[soDataset.CustomerSalesOrderDetailView.Rows.Count - 1];

                        //reallocate for new qty with diff wh
                        _facadeSalesOrder.AllocateStock(newRow);

                        //reassign row allocated for assessment...
                        //always assign the last row as this is the location of the newly added row (Count - 1)
                        newRow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[soDataset.CustomerSalesOrderDetailView.Rows.Count - 1];

                        //Initialize row...
                        CleanItemLine(newRow);

                        //if qtyallocated = 0, delete row
                        if (newRow.QuantityAllocated == 0)
                        {
                            soDataset.CustomerSalesOrderDetailView.Rows.Remove(newRow);
                        }
                        else
                        {
                            //increment total qty allocated
                            qtyAlloc += newRow.QuantityAllocated;
                        }

                        if (qtyOrigOrd == qtyAlloc) { break; } //if fully allocated, simply exit the loop.
                    }
                }

                //Save allocated qty to ecommerce shopping cart
                StoreAllocatedQuantities(soDataset, drow.ItemCode, qtyAlloc);

                //Update the root item's quantity order to reflect the missing quantity unallocated...
                decimal totalAlloc = (decimal)soDataset.CustomerSalesOrderDetailView.Compute("SUM(QuantityAllocated)", string.Format("ItemCode = '{0}'", drow.ItemCode));
                foreach (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow2 in soDataset.CustomerSalesOrderDetailView.Rows)
                {
                    if (drow2.ItemCode != drow.ItemCode) continue;
                    
                    if (drow2.LineNum == drow.LineNum)
                    {
                        //root item
                        drow.BeginEdit();
                        //the quantity assigned to the root item should also include the un-allocated qty for its split items
                        drow.QuantityOrdered = qtyOrigOrd - (totalAlloc - drow.QuantityAllocated);
                        drow.EndEdit();
                    }
                    else
                    {
                        //split items
                        drow2.BeginEdit();
                        drow2.QuantityOrdered = drow2.QuantityAllocated;
                        drow2.EndEdit();
                    }

                    //Update Kit Detail
                    if (drow2.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                    {
                        UpdateKitDetailView(drow2, soDataset);
                    }
                }

                //track total qty to reserve
                qtyToReserve = qtyOrigOrd - totalAlloc;

                reserveInfo[rowIndex] = new Object[] { drow.ItemCode, qtyToReserve, (decimal)0, qtyOrigOrd };                
            }
            //END ALLOCATION CODE ------------------------------------------------------


            //START RESERVATION CODE ------------------------------------------------------

            //Start reserving already included line item rows
            //--------------------------------------------------------
            decimal currReservedQty = 0;

            //Modify shipping date to max date to allow the system to retrieve all POs available for reservation
            soDataset.CustomerSalesOrderView[0].BeginEdit();
            soDataset.CustomerSalesOrderView[0][soDataset.CustomerSalesOrderView.ShippingDateColumn.ColumnName] = Interprise.Framework.Base.Shared.Const.MAX_SMALLDATETIME;
            soDataset.CustomerSalesOrderView[0].EndEdit();

            //Deletes currently stored reservation information to make way for an updated one
            //Also modifies the TransactionCode of the reservation table to match that of the current sales order code.
            ReInitializeReservation(soDataset);

            string currentItemCode = "";

            //Begin reservation for all the line items.
            foreach (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow in soDataset.CustomerSalesOrderDetailView.Rows)
            {
                //After the allocation has completed, we need to reserve items                
                decimal balance = 0;

                //get reserveInfo
                for (int resIdx = 0; resIdx <= reserveInfo.Length - 1; resIdx++)
                {
                    if (reserveInfo[resIdx][0].ToString() != drow.ItemCode) continue;

                    if (currentItemCode != drow.ItemCode) 
                    { 
                        currReservedQty = 0; 
                    }

                    balance = (decimal)reserveInfo[resIdx][1] - currReservedQty;
                    break;

                }

                //if there are no longer any qty left for reservation, simply move on to the next item.
                if (balance == 0) { continue; }

                drow.QuantityOrdered = drow.QuantityAllocated + System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0));
                drow.QuantityOrdered += balance;
                drow.DueDate = Interprise.Framework.Base.Shared.Const.MAX_SMALLDATETIME;

                //begin qty reservation
                _facadeSalesOrder.LoadItemReservation(drow);
                
                //begin qty reservation
                _facadeSalesOrder.ReserveStock(drow);
                //store reservation data
                if (System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0)) > 0)
                {
                    //track reserved qty
                    currReservedQty += drow.QuantityReserved;

                    // update current detail record
                    currentItemCode = drow.ItemCode;

                    //update reserveInfo
                    for (int resIdx = 0; resIdx <= reserveInfo.Length - 1; resIdx++)
                    {
                        if (reserveInfo[resIdx][0].ToString() != drow.ItemCode) continue;

                        reserveInfo[resIdx][2] = (decimal)currReservedQty;
                        break;
                    }

                    if (drow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                    {
                        StoreReservedKitQuantity(drow);
                    }
                    else
                    {
                        StoreReservedQuantity(drow.ItemCode, true);
                    }
                }

                drow.QuantityOrdered = drow.QuantityAllocated + System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0));                
                //Re-initialize row...
                CleanItemLine(drow);

                //Update Kit Detail
                if (drow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                {
                    UpdateKitDetailView(drow, soDataset);
                }
                                
            }
            //--------------------------------------------------------


            //Search other POs from warehouses not in SO
            //--------------------------------------------------------
            decimal currResQty = 0;
            decimal totalResQty = 0;
            for (int resIdx = 0; resIdx <= reserveInfo.Length - 1; resIdx++)
            {
                totalResQty = (decimal)reserveInfo[resIdx][1];
                currResQty = (decimal)reserveInfo[resIdx][2];

                //check for item remaining qty unallocated/unreserved
                if (totalResQty > currResQty)
                {
                    SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow = null;
                    string str = string.Format("ItemCode = '{0}'", reserveInfo[resIdx][0]);                    
                    DataRow[] itemRows = soDataset.CustomerSalesOrderDetailView.Select(str, "LineNum");
                    
                    //NOTE: We only need 1 instance of the item since we're going to filter out the warehouses thru Select looping all warehouses read                   
                    drow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)itemRows[0];

                    //Get cart item GUID
                    var itemGuid = new Guid();
                    var cartItem = this.CartItems.FirstOrDefault(item => item.ItemCode == drow.ItemCode);
                    if (cartItem != null) { itemGuid = cartItem.Id; }

                    //check for other warehouse locations if there are any POs available for reservation
                    foreach (DataRow drWarehouse in dtWarehouse.Rows)
                    {
                        //get order detail row instance for splits
                        string strExp = string.Format("ItemCode = '{0}' and WarehouseCode = '{1}'", drow.ItemCode, drWarehouse["WarehouseCode"].ToString());
                        DataRow[] lineItem = soDataset.CustomerSalesOrderDetailView.Select(strExp);

                        //If result.length == 0, it means that the row does not exist yet and is exactly what need
                        if (lineItem.Length == 0)
                        {
                            //Add a new line item with a new warehouse code and quantity
                            AddLineItem(drow.ItemCode, drow.UnitMeasureCode, drWarehouse, totalResQty - currResQty, drow.ItemDescription, itemGuid);

                            //get the instance of the newly added reservation row
                            var newRow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[soDataset.CustomerSalesOrderDetailView.Rows.Count - 1];
                                                        
                            //Initialize row...
                            CleanItemLine(newRow);

                            //begin qty reservation
                            _facadeSalesOrder.LoadItemReservation(newRow);

                            //begin qty reservation
                            _facadeSalesOrder.ReserveStock(newRow);

                            if (System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(newRow["QuantityReserved"], 0)) > 0)
                            {
                                //modify qty ordered to be the same as the reserved then recompute
                                newRow.BeginEdit();
                                newRow.QuantityOrdered = newRow.QuantityReserved;
                                newRow.EndEdit();

                                CleanItemLine(newRow);

                                //Update Kit Detail
                                if (newRow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                                {
                                    UpdateKitDetailView(newRow, soDataset);
                                }
                                
                                //update current reserved qty
                                currResQty += newRow.QuantityReserved;
                                    
                                //update reserveInfo
                                reserveInfo[resIdx][2] = currResQty;

                                if (newRow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                                {
                                    StoreReservedKitQuantity(newRow);
                                }
                                else
                                {
                                    StoreReservedQuantity(newRow.ItemCode, true);
                                }

                                //if qty fully reserved, exit. otherwise, search for possible POs from other warehouses
                                if (currResQty == totalResQty) { break; }
                            }
                            else
                            {
                                //remove row if its not reserved
                                if (System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(newRow["QuantityReserved"], 0)) == 0) { soDataset.CustomerSalesOrderDetailView.Rows.Remove(newRow); }
                            }
                        }
                    }
                }
            }
            //--------------------------------------------------------


            //Reservation clean-up
            //--------------------------------------------------------
            //check if after looking through all warehouses there are still qty not reserved
            for (int rowIndex = 0; rowIndex <= rowCount - 1; rowIndex++)
            {
                var drow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[rowIndex];

                //get remaining reserve and reserved qty
                decimal finalNeededReserved = 0;
                decimal finalReserved = 0;
                for (int resIdx = 0; resIdx <= reserveInfo.Length - 1; resIdx++)
                {
                    if (reserveInfo[resIdx][0].ToString() != drow.ItemCode) continue;
                    finalNeededReserved = (decimal)reserveInfo[resIdx][1];
                    finalReserved = (decimal)reserveInfo[resIdx][2];
                    break;
                }

                if (finalReserved < finalNeededReserved)
                {
                    decimal remResQty = finalNeededReserved - finalReserved;
                    drow.BeginEdit();
                    //since we modified the root item's quantity ordered for the process of allocation and reservation
                    //we need to restore the qty removed but are unallocated or unreserved...
                    drow.QuantityOrdered += remResQty;
                    drow.EndEdit();

                    //Update Kit Detail
                    if (drow.ItemType == Interprise.Framework.Base.Shared.Const.ITEM_TYPE_KIT)
                    {
                        UpdateKitDetailView(drow, soDataset);
                    }
                }
            }
            //--------------------------------------------------------

            //END RESERVATION CODE ------------------------------------------------------

            //Full Allocation and Reservation Clean-up
            //--------------------------------------------------------
            for (int rowIndex = soDataset.CustomerSalesOrderDetailView.Rows.Count - 1; rowIndex >= 0; rowIndex--)
            {
                var drow = (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow)soDataset.CustomerSalesOrderDetailView.Rows[rowIndex];
                if (drow.QuantityOrdered == 0  && drow.QuantityAllocated == 0 && System.Convert.ToDecimal(Interprise.Framework.Base.Shared.Common.IsNull(drow["QuantityReserved"], 0)) == 0) {
                    DataRow[] splitRow = soDataset.CustomerSalesOrderDetailView.Select(string.Format("ItemCode = '{0}' and LineNum <> {1}", drow.ItemCode, drow.LineNum));

                    //Check if there are split rows for the root item. If none, do not delete the root item
                    if (splitRow.Length > 0)
                    {
                        //Since we will be deleting the root item, an exception on redering the page will be encountered.
                        //To fix this, we will replace the
                        var cartItem = this.CartItems.FirstOrDefault(item => item.ItemCode == drow.ItemCode);
                        if (cartItem != null) 
                        {
                            cartItem.AssociatedLineItemRow = splitRow[0] as SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow;
                        }

                        DeleteRelatedRows(drow, soDataset);
                        soDataset.CustomerSalesOrderDetailView.Rows.Remove(drow);
                    }                    
                }
            }
            //--------------------------------------------------------

            //Recompute whole order
            //---------------------------------------------------------------------------
            foreach (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drowItems in soDataset.CustomerSalesOrderDetailView.Rows)
            {
                _facadeSalesOrder.Compute(drowItems, Interprise.Framework.Base.Shared.Enum.TransactionType.SalesOrder);
            }
            
            _facadeSalesOrder.ComputeTotals(false, false, true);
            //---------------------------------------------------------------------------
        }

        private void UpdateKitDetailView(SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow, SalesOrderDatasetGateway soDataset)
        {
            var kitDetailRows = soDataset.CustomerItemKitDetailView.Select(string.Format("ItemKitCode = '{0}' and LineNum = {1}", drow.ItemCode, drow.LineNum));
            for (int kitCtr = 0; kitCtr <= kitDetailRows.Length - 1; kitCtr++)
            {
                var kitDetailRow = kitDetailRows[kitCtr] as SalesOrderDatasetGateway.CustomerItemKitDetailViewRow;
                kitDetailRow.QuantityOrdered = drow.QuantityOrdered * drow.UnitMeasureQty * kitDetailRow.QuantityPerKit * kitDetailRow.UnitMeasureQty;
            }
        }

        private void CleanItemLine(SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow)
        {
            drow.BeginEdit();
            drow.OldWarehouseCode = string.Empty;
            drow.OriginalQuantityOrdered = 0;
            drow.OriginalQuantityAllocated = 0;            
            drow.OriginalQuantityReservedInBase = 0;
            drow.OriginalQuantityOrdered = 0;
            drow.DueDate = Interprise.Framework.Base.Shared.Const.MAX_SMALLDATETIME;
            drow.EndEdit();
        }

        private void DeleteRelatedRows(SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow, SalesOrderDatasetGateway soDataset)
        {
            //Delete CustomerSalesOrderWorkflow            
            for (int wfCtr = soDataset.CustomerSalesOrderWorkflowView.Rows.Count - 1; wfCtr >= 0; wfCtr--)
            {
                bool wfUsed = soDataset.CustomerSalesOrderDetailView.Rows
                                .OfType<SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow>()
                                .Any(row => row.WarehouseCode == soDataset.CustomerSalesOrderWorkflowView.Rows[wfCtr]["WarehouseCode"].ToString());
                if(wfUsed) return;

                //delete unused warehouse workflow
                if (!wfUsed) { soDataset.CustomerSalesOrderWorkflowView.Rows[wfCtr].Delete(); }
            }
            
            //Delete TransactionItemTaxDetail
            DataRow[] taxDetails = soDataset.TransactionItemTaxDetailView.Select(string.Format("ItemCode = '{0}' and LineNum = {1}", drow.ItemCode, drow.LineNum));
            for (int taxCtr = taxDetails.Length- 1; taxCtr >= 0; taxCtr--)
            {
                taxDetails[taxCtr].Delete();
            }

            //Delete CustomerItemKitDetail
            DataRow[] kitDetails = soDataset.CustomerItemKitDetailView.Select(string.Format("ItemKitCode = '{0}' and LineNum = {1}", drow.ItemCode, drow.LineNum));
            for (int kitCtr = kitDetails.Length - 1; kitCtr >= 0; kitCtr--)
            {
                kitDetails[kitCtr].Delete();
            }

        }

        private bool HasAllocatedQuantity()
        {
            var soDataset = (SalesOrderDatasetGateway)_facadeSalesOrder.CurrentDataset;

            bool result = soDataset.CustomerSalesOrderDetailView.Rows
                                .OfType<SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow>()
                                .Any(row => row.QuantityAllocated > 0);

            soDataset.Dispose();
            soDataset = null;

            return result;
        }

        private void ReInitializeReservation(SalesOrderDatasetGateway soDataset, bool isClearReservation = true)
        {
            var sqlStr = new StringBuilder();

            //delete existing reservation data
            if (isClearReservation)
            {
                sqlStr.AppendFormat("DELETE [EcommerceShoppingCartReservation] WHERE ContactCode = {0}; ", DB.SQuote(ThisCustomer.ContactCode));
                DB.ExecuteSQL(sqlStr.ToString());
            }

            //update reservation dataset
            var reserveDataset = (Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway)_facadeSalesOrder.ReservationFacade.CurrentDataset;
            foreach (Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway.SupplierPurchaseOrderReservationViewRow drow in reserveDataset.Tables[Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway.SUPPLIERPURCHASEORDERRESERVATIONVIEW_TABLE].Rows)
            {
                drow.BeginEdit();
                drow.TransactionCode = soDataset.CustomerSalesOrderView.Rows[0][soDataset.CustomerSalesOrderView.SalesOrderCodeColumn.ColumnName].ToString();
                drow.EndEdit();
            }

            foreach (Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway.CustomerKitTransactionReservationViewRow drow in reserveDataset.Tables[Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway.CUSTOMERKITTRANSACTIONRESERVATIONVIEW_TABLE].Rows)
            {
                drow.BeginEdit();
                drow.TransactionCode = soDataset.CustomerSalesOrderView.Rows[0][soDataset.CustomerSalesOrderView.SalesOrderCodeColumn.ColumnName].ToString();
                drow.EndEdit();
            }

            //object disposal/dereference
            reserveDataset.Dispose();
            reserveDataset = null;
        }

        private void StoreAllocatedQuantities(Interprise.Framework.Customer.DatasetGateway.SalesOrderDatasetGateway soDataset, string  itemCode = "", decimal qtyAlloc = 0)
        {
            var sqlStr = new StringBuilder();
            if (itemCode == string.Empty)
            {
                var rows = soDataset.CustomerSalesOrderDetailView.Rows;

                foreach (SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow drow in rows)
                {
                    sqlStr.AppendFormat("UPDATE EcommerceShoppingCart SET AllocatedQty = {0} WHERE ContactCode = {1} AND ItemCode = {2}; ", drow.QuantityAllocated, DB.SQuote(ThisCustomer.ContactCode), DB.SQuote(drow.ItemCode));

                    //update cart item with the allocated qty
                    foreach (var cart in this.CartItems)
                    {
                        if (cart.ItemCode != drow.ItemCode) continue;
                        
                        cart.m_AllocatedQty = drow.QuantityAllocated; 
                    }
                }
            }
            else 
            {
                sqlStr.AppendFormat("UPDATE EcommerceShoppingCart SET AllocatedQty = {0} WHERE ContactCode = {1} AND ItemCode = {2}; ", qtyAlloc, DB.SQuote(ThisCustomer.ContactCode), DB.SQuote(itemCode));

                //update cart item with the allocated qty
                foreach (CartItem cart in this.CartItems)
                {
                    if (cart.ItemCode != itemCode) continue;
                     
                    cart.m_AllocatedQty = qtyAlloc; 
                }
            }

            DB.ExecuteSQL(sqlStr.ToString());
        }

        private void StoreReservedQuantity(string itemCode, bool isClearReservation = false)
        {
            //retrieve the reservation facade's reservation dataset
            var reserveDataset = (Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway)_facadeSalesOrder.ReservationFacade.CurrentDataset;
            //filter the dataset of which rows are reserved
            DataRow[] reservedTrans = reserveDataset.SupplierPurchaseOrderReservationView.Select(string.Format("{0} > 0 and {1} = '{2}'", reserveDataset.SupplierPurchaseOrderReservationView.ReserveQtyColumn.ColumnName, reserveDataset.SupplierPurchaseOrderReservationView.ItemCodeColumn.ColumnName, itemCode));

            var sqlStr = new StringBuilder();

            if (reservedTrans.Length > 0)
            {
                if (isClearReservation) {
                    sqlStr.AppendFormat("DELETE [EcommerceShoppingCartReservation] WHERE ContactCode = {0} AND ItemCode = {1}; ", DB.SQuote(ThisCustomer.ContactCode), DB.SQuote(itemCode));
                } 
                foreach (DataRow drowReserved in reservedTrans)
                {
                    //insert reservation data for the item
                    sqlStr.AppendFormat("INSERT INTO [EcommerceShoppingCartReservation] (ContactCode, ItemCode, ReservedQty, DueDate) VALUES ({0}, {1}, {2}, {3}); ",
                        DB.SQuote(ThisCustomer.ContactCode),
                        DB.SQuote(itemCode),
                        DB.SQuote(System.Convert.ToDecimal(drowReserved["ReserveQty"]).ToString()),
                        DB.SQuote(System.Convert.ToDateTime(drowReserved["DueDate"]).ToString(System.Globalization.DateTimeFormatInfo.InvariantInfo.ShortDatePattern)));
                }
            }

            //execute sqlStr
            DB.ExecuteSQL(sqlStr.ToString());
            //object disposal/dereference
            reserveDataset.Dispose();
            reserveDataset = null;
        }

        private void StoreReservedKitQuantity(SalesOrderDatasetGateway.CustomerSalesOrderDetailViewRow itemRow)
        {
            //retrieve the reservation facade's reservation dataset
            Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway reserveDataset = (Interprise.Framework.Base.DatasetGateway.ReservationDatasetGateway)_facadeSalesOrder.ReservationFacade.CurrentDataset;
            //filter the dataset of which rows are reserved
            DataRow[] reservedTrans = reserveDataset.CustomerKitTransactionReservationView.Select(string.Format("{0} > 0 and {1} = '{2}'", reserveDataset.CustomerKitTransactionReservationView.ReserveQtyColumn.ColumnName, reserveDataset.CustomerKitTransactionReservationView.KitCodeColumn.ColumnName, itemRow.ItemCode), reserveDataset.CustomerKitTransactionReservationView.DueDateColumn.ColumnName);

            var sqlStr = new StringBuilder();
            if (reservedTrans.Length > 0)
            {
                //foreach (DataRow drowReserved in reservedTrans)
                //{
                    //insert reservation data for the item
                    sqlStr.AppendFormat("INSERT INTO [EcommerceShoppingCartReservation] (ContactCode, ItemCode, ReservedQty, DueDate) VALUES ({0}, {1}, {2}, {3}); ",
                        DB.SQuote(ThisCustomer.ContactCode),
                        DB.SQuote(itemRow.ItemCode),
                        DB.SQuote(itemRow.QuantityReserved.ToString()),
                        DB.SQuote(System.Convert.ToDateTime(reservedTrans[reservedTrans.Length - 1]["DueDate"]).ToShortDateString()));
                //}
            }

            //execute sqlStr
            DB.ExecuteSQL(sqlStr.ToString());
            //object disposal/dereference
            reserveDataset.Dispose();
            reserveDataset = null;
        }

        private static DataSet dsWarehouse = null;
        private static DataTable GetWarehouse() {
            if (dsWarehouse == null)
            {
                dsWarehouse = Interprise.Facade.Base.SimpleFacade.Instance.LoadDataSet(CommandType.Text,
                    "SELECT * FROM InventoryWarehouse with (NOLOCK) WHERE IsActive = 1 ",
                    new string[] { "InventoryWarehouse" }, null);
            }
            return dsWarehouse.Tables["InventoryWarehouse"];
        }

        private static string[] GetWarehouseFiltered(DataTable dtWarehouse, string whfilter)
        {
            string[] filteredWH = new string[dtWarehouse.Rows.Count-1];
            string filterStr = string.Format("WarehouseCode <> '{0}'", whfilter);
            DataRow[] whRows = dtWarehouse.Select(filterStr);

            for (int whCtr = 0; whCtr <= whRows.Length - 1; whCtr++) {
                filteredWH[whCtr] = whRows[whCtr]["WarehouseCode"].ToString();
            }

            return filteredWH;
        }

        private static string GetPreferredGateway()
        {
            return GetPrefferedGatewayInfo()[0];
        }

        private static string[] GetPrefferedGatewayInfo()
        {
            string[] info = new string[] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty};

            bool hasGatewayDefinedInWebsite = false;

            if (System.Web.HttpContext.Current == null)
            {
                hasGatewayDefinedInWebsite = false;
            }

            // First try the WebSite if it has one defined            
            using (var con = DB.NewSqlConnection())
            {
                con.Open();
                using (var reader = DB.GetRSFormat(con, "EcommerceGetCreditCardGatewayByWebsite @WebsiteCode = {0}", DB.SQuote(InterpriseHelper.ConfigInstance.WebSiteCode)))
                {
                    if (reader.Read())
                    {
                        hasGatewayDefinedInWebsite = true;
                        info[0] = DB.RSField(reader, "CreditCardGateway");
                        info[1] = DB.RSFieldBool(reader, "IsCustom").ToString();
                        info[2] = string.Empty;
                        info[3] = DB.RSField(reader, "CreditCardGatewayAssemblyName");
                        info[4] = DB.RSField(reader, "MerchantLogin");
                    }
                }
            }

            // If we don't have one defined let's try the Customer's Payment Term instead
            if (!hasGatewayDefinedInWebsite)
            {
                using (var con = DB.NewSqlConnection())
                {
                    con.Open();
                    using (var reader = DB.GetRSFormat(con, "eCommerceGetCreditCardGatewayByPaymentTerm @PaymentTermCode = {0}", DB.SQuote(Customer.Current.PaymentTermCode)))
                    {
                        if (reader.Read())
                        {
                            hasGatewayDefinedInWebsite = true;
                            info[0] = DB.RSField(reader, "CreditCardGateway");
                            info[1] = DB.RSFieldBool(reader, "IsCustom").ToString();
                            info[2] = string.Empty;
                            info[3] = DB.RSField(reader, "CreditCardGatewayAssemblyName");
                            info[4] = DB.RSField(reader, "MerchantLogin");
                        }
                    }
                }
            }
            return info;
        }

        private static ICreditCardGatewayInterface GetGatewayProcessorCore()
        {
            string[] info = GetPrefferedGatewayInfo();
            ICreditCardGatewayInterface gatewayProcessor = null;
            try
            {
                var gatewayInterface = Interprise.Facade.Base.SimpleFacade.Instance.DeserializeFormSectionPlugin(info, new object[] { }) as Interprise.Extendable.Base.Presentation.Customer.CreditCardGateway.ICreditCardGatewayInterface;
                if (null != gatewayInterface)
                {
                    gatewayProcessor = gatewayInterface.CreditCardGatewayFacade;
                }
            }
            catch (Exception)
            {
                throw new Exception("Unable to instantiate Default Credit Card Gateway");
            }

            return gatewayProcessor;
        }

        private string ProcessPayment(bool convertToInvoiceAndCapture,
            Address billingAddress,
            GatewayResponse withGatewayAuthResponse,
            string receiptCode,
            string salesOrderCode,
            bool is3DsecondAuth)
        {
            string status = AppLogic.ro_OK;

            /***************************************************************
            *  2. Process the Receipt
            * *************************************************************/

            // NOTE : 
            //  Process only the receipt if the Payment Method is Credit Card
            //  If the Payment Method is Cash or Cheque, we only create a Sales Order
            var term = PaymentTermDTO.Find(ThisCustomer.PaymentTermCode);

            if (term.PaymentTermCode != AppLogic.AppConfig("PaymentTermCodeZeroDollarOrder") &&
                false == "REQUEST QUOTE".Equals(term.PaymentTermCode) &&
                false == "PURCHASE ORDER".Equals(term.PaymentTermCode) &&
                term.PaymentMethod == InterpriseHelper.PAYMENT_METHOD_CREDITCARD)
            {
                var gatewayReceiptDataset = new ReceiptDatasetGateway();
                var facadeReceipt = new ReceiptFacade(gatewayReceiptDataset);

                var facadeCustomerReceiptList = new ListControlFacade();
                var datasetCustomerReceipt = new DataSet();
                var customerReceiptBaseDataset = new BaseDataset();

                // retrieve the customer information
                string customerInfoQuery = string.Format("CustomerCode = {0}",
                                            DB.SQuote(CommonLogic.IIF(ThisCustomer.IsNotRegistered, ThisCustomer.AnonymousCustomerCode, ThisCustomer.CustomerCode)));

                //deadlock
                int nooftries = 0;
                while (nooftries < AppLogic.InterpriseExceptionFacadeNumberOfTries)
                {
                    try
                    {
                        datasetCustomerReceipt = facadeCustomerReceiptList.ReadSearchResultsData("CustomerActiveCustomersView",
                                                    customerInfoQuery,
                                                    1,
                                                    false,
                                                    string.Empty,
                                                    ref customerReceiptBaseDataset,
                                                    true);
                        break;
                    }
                    catch (SqlException ex)
                    {
                        if (ex.ErrorCode == 1205)
                        {
                            nooftries += 1;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                string cardExtraCode = AppLogic.GetCardExtraCodeFromSession(ThisCustomer);

                DataRow rowCustomer = customerReceiptBaseDataset.Tables["CustomerActiveCustomersView"].Rows[0];

                facadeReceipt.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt;

                if (is3DsecondAuth)
                {
                    DataSet ds = Interprise.Facade.Base.SimpleFacade.Instance.LoadDataSet(CommandType.StoredProcedure,
                                                                                          "EcommerceGetCustomerPayment",
                                                                                           new string[] { Interprise.Framework.Customer.Shared.Const.CUSTOMERTRANSACTIONRECEIPTVIEW_TABLE, 
                                                                                                          Const.CUSTOMERPAYMENT_TABLE, Const.CUSTOMERCARDPAYMENT_TABLE, Const.PAYMENTMETHODVIEW_TABLE, 
                                                                                                          "DefaultAccount", "HasReserved", Interprise.Framework.Customer.Shared.Const.CUSTOMERTRADINGINFOVIEW_TABLE },
                                                                                           new string[][] { new string[] {"@DocumentCode", salesOrderCode},
                                                                                                            new string[] {"@CustomerCode", ThisCustomer.CustomerCode}},
                                                                                           Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                    if (ds.Tables.Contains(Interprise.Framework.Customer.Shared.Const.CUSTOMERTRANSACTIONRECEIPTVIEW_TABLE))
                    {
                        gatewayReceiptDataset.Merge(ds.Tables[Interprise.Framework.Customer.Shared.Const.CUSTOMERTRANSACTIONRECEIPTVIEW_TABLE]);
                    }
                    _facadeSalesOrder.IsTransactionReceiptLoaded = true;
                    if (ds.Tables.Contains("DefaultAccount") && ds.Tables["DefaultAccount"].Rows.Count > 0 & ds.Tables.Contains("HasReserved"))
                    {
                        facadeReceipt.SetSettings(Convert.ToString(ds.Tables["DefaultAccount"].Rows[0][0]), ds.Tables["HasReserved"].Rows.Count > 0);
                    }

                    gatewayReceiptDataset.PaymentMethodView.Clear();
                    gatewayReceiptDataset.CustomerPayment.Clear();
                    gatewayReceiptDataset.CustomerCardPayment.Clear();

                    facadeReceipt.RemovePaymentTableRelation();

                    if (ds.Tables.Contains(Interprise.Framework.Base.Shared.Const.CUSTOMERPAYMENT_TABLE))
                    {
                        gatewayReceiptDataset.Merge(ds.Tables[Interprise.Framework.Base.Shared.Const.CUSTOMERPAYMENT_TABLE]);
                    }
                    if (ds.Tables.Contains(Interprise.Framework.Base.Shared.Const.CUSTOMERCARDPAYMENT_TABLE))
                    {
                        gatewayReceiptDataset.Merge(ds.Tables[Interprise.Framework.Base.Shared.Const.CUSTOMERCARDPAYMENT_TABLE]);
                    }
                    if (ds.Tables.Contains(Const.PAYMENTMETHODVIEW_TABLE))
                    {
                        gatewayReceiptDataset.Merge(ds.Tables[Const.PAYMENTMETHODVIEW_TABLE]);
                    }

                    if (!facadeReceipt.DisableRelationInitialization)
                    {
                        facadeReceipt.InitializePaymentTableRelation();
                    }

                    facadeReceipt.RestoreCV();
                }
                else
                {
                    facadeReceipt.AddReceipt(new DataRow[] { rowCustomer },
                    Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt,
                    true,
                    _facadeSalesOrder.GetCalculatedTotalDue(),
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode,
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode,
                    _gatewaySalesOrderDataset.CustomerSalesOrderView[0].ContactCode);
                }

                gatewayReceiptDataset.CustomerCardPayment[0].CardPaymentCode = receiptCode;

                var rowPayMethod = gatewayReceiptDataset.PaymentMethodView[0];

                if (ThisCustomer.IsNotRegistered)
                {
                    // NOTE:
                    //  This should just set the default values
                    facadeReceipt.AssignPaymentMethodDefaults(
                        rowPayMethod,
                        _gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode
                    );

                    // we then will manually assign the credit card details...
                    rowPayMethod.BeginEdit();

                    // NOTE: 
                    //  settting IsCardOnFile to false MUST come first
                    //  Order is important here since internally it modifies other fields
                    //  Setting this column's value at the last would result in a different behaviour
                    rowPayMethod.IsCardOnFile = false;

                    // get payment type detail for IsDeposited
                    string isDeposited = Boolean.TrueString;
                    isDeposited = facadeReceipt.GetField("IsDeposited",
                                                          Interprise.Framework.Base.Shared.Const.SYSTEMPAYMENTTYPE_TABLE,
                                                          String.Format("{0}='{1}'", Interprise.Framework.Base.Shared.Const.SYSTEMPAYMENTTYPE_PAYMENTTYPECODE_COLUMN,
                                                          Convert.ToString(Interprise.Framework.Base.Shared.Common.IsNull(gatewayReceiptDataset.PaymentMethodView[0][gatewayReceiptDataset.PaymentMethodView.PaymentTypeCodeColumn.ColumnName], String.Empty))));

                    if (isDeposited == null || String.Compare(isDeposited, Boolean.FalseString, true) == 0)
                    {
                        rowPayMethod.Account = Interprise.Framework.Base.Shared.Const.PAYMENT_ACCOUNT_UNDEPOSITED;
                    }
                    else
                    {
                        rowPayMethod.Account = Interprise.Framework.Base.Shared.Const.PAYMENT_ACCOUNT;
                    }

                    rowPayMethod.CreditCardCode = string.Empty;
                    rowPayMethod.CreditCardSalt = billingAddress.CardNumberSalt;
                    rowPayMethod.CreditCardEmail = billingAddress.EMail;
                    rowPayMethod.CreditCardIV = billingAddress.CardNumberIV;
                    rowPayMethod.CreditCardName = billingAddress.CardName;

                    // NOTE:
                    // IS Always assume that the credit card being passed here is encrypted
                    // thus the need for the next line below
                    rowPayMethod.CreditCard = facadeReceipt.EncryptCardNumber(billingAddress.CardNumber, rowPayMethod);

                    rowPayMethod.CreditCardAddress = billingAddress.Address1;
                    rowPayMethod.CreditCardCity = billingAddress.City;
                    rowPayMethod.CreditCardState = billingAddress.State;
                    rowPayMethod.CreditCardPostalCode = billingAddress.PostalCode;
                    rowPayMethod.CreditCardCountry = billingAddress.Country;
                    if (ThisCustomer.IsNotRegistered && ThisCustomer.ThisCustomerSession["paypalfrom"] != null && (ThisCustomer.ThisCustomerSession["paypalfrom"] == "shoppingcart" || ThisCustomer.ThisCustomerSession["paypalfrom"] == "checkoutpayment" || ThisCustomer.ThisCustomerSession["paypalfrom"] == "onepagecheckout"))
                    {
                        DateTime now = DateTime.Now;
                        rowPayMethod.CreditCardExpMon = now.ToString("MMM");
                        rowPayMethod.CreditCardExpYear = now.AddYears(1).Year.ToString();
                    }
                    else
                    {
                        rowPayMethod.CreditCardExpMon = InterpriseHelper.ToInterpriseExpMonth(billingAddress.CardExpirationMonth);
                        rowPayMethod.CreditCardExpYear = billingAddress.CardExpirationYear;
                    }

                    if (AppLogic.AppConfigBool("ShowCardStartDateFields"))
                    {
                        rowPayMethod.CreditCardStartMon = InterpriseHelper.ToInterpriseExpMonth(billingAddress.CardStartMonth);
                        rowPayMethod.CreditCardStartYear = billingAddress.CardStartYear;

                        // same reason for credit card applies here
                        rowPayMethod.CreditCardIssueNumber = facadeReceipt.EncryptCardIssueNumber(billingAddress.CardIssueNumber, rowPayMethod);
                    }

                    rowPayMethod.CreditCardTelephone = billingAddress.Phone;
                    rowPayMethod.CreditCardTelephoneExtension = string.Empty;
                    rowPayMethod.CreditCardType = billingAddress.CardType;
                    rowPayMethod.CreditCardTypeDescription = billingAddress.CardType;
                    rowPayMethod.EndEdit();
                }
                else
                {
                    // Registered Customer..........

                    // Code inside this function should retrieve the card number
                    // information of the customer if it has a default one...
                    if (!is3DsecondAuth)
                    {
                        facadeReceipt.AssignPaymentMethodDefaults(
                            rowPayMethod,
                            _gatewaySalesOrderDataset.CustomerSalesOrderView[0].PaymentTermCode
                        );
                    }
                }

                // NOTE :   this should recompute for AmountPaid and AmountPaidRate
                //          both CustomerPayment and PaymentMethod table
                rowPayMethod.AmountPaidRate = _facadeSalesOrder.GetCalculatedTotalDue();

                string[] receiptRelatedTables = facadeReceipt.get_RelatedTables(Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt);
                string[][] receiptCommands = facadeReceipt.CreateParameterSet(receiptRelatedTables);

                facadeReceipt.UpdateDataSet(receiptCommands,
                                            Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerReceipt,
                                            string.Empty,
                                            false);

                //Capture the credit card number from the payment page and encrypt it so that the gateway can capture from that credit card
                rowPayMethod.CreditCardSalt = ThisCustomer.ThisCustomerSession["CardNumberSalt"];
                rowPayMethod.CreditCardIV = ThisCustomer.ThisCustomerSession["CardNumberIV"];
                rowPayMethod.CreditCard = ThisCustomer.ThisCustomerSession["CardNumber"];
                if (ThisCustomer.IsNotRegistered && ThisCustomer.ThisCustomerSession["paypalfrom"] != null && (ThisCustomer.ThisCustomerSession["paypalfrom"] == "shoppingcart" || ThisCustomer.ThisCustomerSession["paypalfrom"] == "checkoutpayment" || ThisCustomer.ThisCustomerSession["paypalfrom"] == "onepagecheckout"))
                {
                    DateTime now = DateTime.Now;
                    rowPayMethod.CreditCardExpMon = now.ToString("MMM");
                    rowPayMethod.CreditCardExpYear = now.AddYears(1).Year.ToString();
                }
                else
                {
                    rowPayMethod.CreditCardExpMon = InterpriseHelper.ToInterpriseExpMonth(billingAddress.CardExpirationMonth);
                    rowPayMethod.CreditCardExpYear = billingAddress.CardExpirationYear;
                }
                rowPayMethod.CreditCardCV = billingAddress.CardIssueNumber;

                // this will assign the Card On Name to Credit Card Gateway.
                rowPayMethod.CreditCardName = billingAddress.CardName;
                

                rowPayMethod.CreditCardCV = cardExtraCode;
                rowPayMethod.CreditCardTransactionType = "Auth";

                rowPayMethod.CreditCardCountry = billingAddress.Country;
                rowPayMethod.CreditCardAddress = billingAddress.Address1;
                rowPayMethod.CreditCardCity = billingAddress.City;
                rowPayMethod.CreditCardState = billingAddress.State;
                rowPayMethod.CreditCardPostalCode = billingAddress.PostalCode;
                rowPayMethod.CreditCardTelephone = billingAddress.Phone;
                rowPayMethod.CreditCardEmail = billingAddress.EMail;

                //if (AppLogic.AppConfigBool("ShowCardStartDateFields"))
                //{
                //    string cardIssueNumber = ThisCustomer.ThisCustomerSession["CardIssueNumber"].ToString();
                //    string cardIssueNumberSalt = ThisCustomer.ThisCustomerSession["CardIssueNumberSalt"].ToString();
                //    string cardIsseuNumberIV = ThisCustomer.ThisCustomerSession["CardIssueNumberIV"].ToString();
                //    Interprise.Licensing.Base.Services.CryptoServiceProvider cryptoService = new Interprise.Licensing.Base.Services.CryptoServiceProvider();
                //    cardIssueNumber = cryptoService.Decrypt(Convert.FromBase64String(cardIssueNumber), Convert.FromBase64String(cardIssueNumberSalt), Convert.FromBase64String(cardIsseuNumberIV));
                //    rowPayMethod.CreditCardIssueNumber = cardIssueNumber;
                //}

                //Credit Card Tokenization...............
                if (AppLogic.IsUsingInterpriseGatewayv2() && ThisCustomer.IsRegistered)
                {
                    //Check if billing has existing reference no.
                    #region GetReferenceNo
                    using (var con = DB.NewSqlConnection())
                    {
                        con.Open();
                        using (var reader = DB.GetRSFormat(con, "SELECT InterpriseGatewayRefNo FROM CustomerCreditCard with (NOLOCK) WHERE CreditCardCode={0}", DB.SQuote(billingAddress.AddressID)))
                        {
                            if (reader.Read())
                            {
                                rowPayMethod.InterpriseGatewayRefNo = DB.RSFieldInt(reader, "InterpriseGatewayRefNo");
                            }
                        }
                    }
                    #endregion

                    rowPayMethod.IsCardOnFile = AppLogic.IsSaveCardInfoChecked(ThisCustomer); //if true, refno will be generated upon successful authorzation

                    if (rowPayMethod.InterpriseGatewayRefNo > 0)
                    {
                        rowPayMethod.IsCardOnFile = false; //set to false since refno already exists

                        if (!AppLogic.AppConfigBool("AllowCreditCardInfoSaving"))
                        {
                            rowPayMethod.InterpriseGatewayRefNo = 0;
                            DB.ExecuteSQL("UPDATE CustomerCreditCard SET InterpriseGatewayRefNo=NULL, Vault=NULL WHERE CreditCardCode={0}", DB.SQuote(billingAddress.AddressID));
                        }
                    }

                    //override paymethod billing info
                    rowPayMethod.CreditCardName = billingAddress.CardName;
                    rowPayMethod.CreditCardType = billingAddress.CardType;
                    rowPayMethod.CreditCardStartMon = (billingAddress.CardStartMonth != string.Empty) ? InterpriseHelper.ToInterpriseExpMonth(billingAddress.CardStartMonth) : string.Empty;
                    rowPayMethod.CreditCardStartYear = billingAddress.CardStartYear;
                    rowPayMethod.CreditCardExpMon = InterpriseHelper.ToInterpriseExpMonth(billingAddress.CardExpirationMonth);
                    rowPayMethod.CreditCardExpYear = billingAddress.CardExpirationYear;

                    rowPayMethod.CreditCardCountry = billingAddress.Country;
                    rowPayMethod.CreditCardAddress = billingAddress.Address1;
                    rowPayMethod.CreditCardCity = billingAddress.City;
                    rowPayMethod.CreditCardState = billingAddress.State;
                    rowPayMethod.CreditCardPostalCode = billingAddress.PostalCode;
                    rowPayMethod.CreditCardTelephone = billingAddress.Phone;
                    rowPayMethod.CreditCardEmail = billingAddress.EMail;
                }
                //.....

                string gatewayMessage = string.Empty;
                bool approved = false;
               
                if (withGatewayAuthResponse != null)
                {
                    approved = true;
                    AttachGatewayAuthorization(gatewayReceiptDataset, rowPayMethod, withGatewayAuthResponse, receiptCode);

                    // Set the next IS gateways that will process the capture part
                    // Since now we're just doing AUTH
                    if (!ThisCustomer.ThisCustomerSession["paypalfrom"].IsNullOrEmptyTrimmed())
                    {
                        rowPayMethod.CreditCardGatewayAssemblyName = "Interprise.Presentation.Customer.PaymentGateway.PayPal";
                        rowPayMethod.CreditCardGatewayType = "Interprise.Presentation.Customer.PaymentGateway.PayPal.PayPalGatewayControl";
                    }

                    facadeReceipt.UpdateDataSet(new string[][] { 
                            new string[] { gatewayReceiptDataset.CustomerCardPayment.TableName, 
                                "CreateCustomerCardPayment", 
                                "UpdateCustomerCardPayment", 
                                "DeleteCustomerCardPayment" }, 
                            new string[] { gatewayReceiptDataset.CustomerPayment.TableName, 
                                "CreateCustomerPayment", 
                                "UpdateCustomerPayment", 
                                "DeleteCustomerPayment" }, 
                            new string[] { gatewayReceiptDataset.PaymentMethodView.TableName, 
                                "CreatePaymentMethod", 
                                "UpdatePaymentMethod", 
                                "DeletePaymentMethod" }, 
                            new string[] { gatewayReceiptDataset.CustomerCCAuthResponse.TableName, 
                                "CreateCustomerCCAuthResponse", 
                                "UpdateCustomerCCAuthResponse", 
                                "DeleteCustomerCCAuthResponse" } },
                            facadeReceipt.TransactionType,
                            "Processed credit card payment.",
                            false);

                    status = AppLogic.ro_OK;
                }
                else
                {
                    string prefferredGateway = GetPreferredGateway();
                    var cardGateway = GetGatewayProcessorCore();
                    if (null == cardGateway) throw new Exception("Could not load payment Gateway!!!");

                    rowPayMethod.CreditCardGatewayAssemblyName = cardGateway.CreditCardGatewayAssemblyName;
                    rowPayMethod.CreditCardGatewayType = cardGateway.CreditCardGatewayType;
                    rowPayMethod.WebsiteCode = InterpriseHelper.ConfigInstance.WebSiteCode;

                    using (var facadeCard = new CreditCardAuthorizationFacade(cardGateway))
                    {
                        facadeCard.TransactionType = facadeReceipt.TransactionType;

                        if (prefferredGateway == AppLogic.ro_PROTX)
                        {
                            facadeCard.IsEcommerce = true;
                            facadeCard.Is3DSecure = CommonLogic.StringInCommaDelimitedStringList(billingAddress.CardType, AppLogic.AppConfig("3DSECURE.CreditCardTypes"));
                            if (facadeCard.Is3DSecure && is3DsecondAuth)
                            {
                                facadeCard.Secure3DMD = ThisCustomer.ThisCustomerSession["3DSecure.MD"];
                                facadeCard.Secure3DPAReq = ThisCustomer.ThisCustomerSession["3Dsecure.PaRes"];
                            }
                        }

                        gatewayMessage = facadeCard.AuthorizeCreditCard(facadeReceipt, false, ref approved);

                        if (prefferredGateway == AppLogic.ro_PROTX && facadeCard.Is3DSecure && !is3DsecondAuth)
                        {
                            ThisCustomer.ThisCustomerSession["3DSecure.MD"] = facadeCard.Secure3DMD;
                            ThisCustomer.ThisCustomerSession["3DSecure.ACSUrl"] = facadeCard.Secure3DACSURL;
                            ThisCustomer.ThisCustomerSession["3DSecure.PAReq"] = facadeCard.Secure3DPAReq;
                            ThisCustomer.ThisCustomerSession["3DSecure.CustomerID"] = ThisCustomer.ContactGUID.ToString();
                            ThisCustomer.ThisCustomerSession["3DSecure.OrderNumber"] = salesOrderCode;
                            ThisCustomer.ThisCustomerSession["3DSecure.XID"] = receiptCode;
                        }

                        //Credit Card Tokenization........
                        if (cardGateway.PnRefNo > 0 && AppLogic.IsUsingInterpriseGatewayv2() && gatewayMessage == string.Empty && ThisCustomer.IsRegistered)
                        {
                            //update reference no and vault manually
                            #region Update ReferenceNo
                            if (AppLogic.IsSaveCardInfoChecked(ThisCustomer))
                            {
                                DB.ExecuteSQL("UPDATE CustomerCreditCard SET InterpriseGatewayRefNo={0}, Vault='NMI' WHERE CreditCardCode={1}",
                                    DB.SQuote(cardGateway.PnRefNo.ToString()),
                                    DB.SQuote(billingAddress.AddressID));
                            }
                            #endregion

                            ThisCustomer.ThisCustomerSession["SaveCreditCardChecked"] = string.Empty;
                        }
                        //.....

                        cardGateway.Dispose();
                    }

                    if (!approved)
                    {
                        if (prefferredGateway == AppLogic.ro_PROTX && !is3DsecondAuth  && (CommonLogic.StringInCommaDelimitedStringList(billingAddress.CardType, AppLogic.AppConfig("3DSECURE.CreditCardTypes"))))
                        {
                            status = AppLogic.ro_3DSecure;
                        }
                        else
                        {
                            ThisCustomer.LastGatewayErrorMessage = facadeReceipt.GetLastCreditCardProcessMessage();
                            if (prefferredGateway == AppLogic.ro_PROTX && is3DsecondAuth)
                            {
                                string gatewayResponseCode = facadeReceipt.CurrentDataset.Tables["CustomerCCAuthResponse"].Rows[0]["GatewayResponseCode"].ToString();
                                switch (gatewayResponseCode.ToUpperInvariant())
                                {
                                    case "ERROR":
                                        status = "The transaction encountered an error. Please try again";
                                        break;
                                    case "INVALID":
                                        status = "The card was not accepted. Please try again";
                                        break;
                                    case "NOTAUTHED":
                                        status = "Your card was not authorized for that amount. Please try again";
                                        break;
                                    case "REJECTED":
                                        status = "Your card was not not accepted. Please try again";
                                        break;
                                    default:
                                        status = facadeReceipt.CurrentDataset.Tables["CustomerCCAuthResponse"].Rows[0]["ResponseMsg"].ToString();
                                        break;
                                }
                            }
                            else
                            {
                                status = AppLogic.ro_INTERPRISE_GATEWAY_AUTHORIZATION_FAILED;
                            }

                            string gateway = "Gateway";
                            using (var con = DB.NewSqlConnection())
                            {
                                con.Open();
                                using (var reader = DB.GetRSFormat(con, "SELECT [Description] FROM [CustomerCreditCardGatewayView] with (NOLOCK) WHERE Gateway = {0}", DB.SQuote(prefferredGateway)))
                                {
                                    if (reader.Read())
                                    {
                                        gateway = DB.RSField(reader, "Description");
                                    }
                                }
                            }

                            var transactionFailed = FailedTransaction.New();
                            transactionFailed.CustomerCode = ThisCustomer.CustomerCode;
                            transactionFailed.SalesOrderCode = _gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode;
                            transactionFailed.PaymentGateway = gateway;
                            transactionFailed.PaymentMethod = InterpriseHelper.PAYMENT_METHOD_CREDITCARD;
                            transactionFailed.TransactionCommand = FailedTransaction.NOT_APPLICABLE;
                            transactionFailed.TransactionResult = facadeReceipt.GetLastCreditCardProcessMessage() + gatewayMessage;
                            transactionFailed.IPAddress = ThisCustomer.LastIPAddress;
                            transactionFailed.IsFailed = true;

                            transactionFailed.Record();
                        }
                    }
                }

                /*****************************************************
                 * 3. Associate the receipt with the Sales Order
                *****************************************************/
                var gatewayTransactionReceiptDataset = _facadeSalesOrder.TransactionReceiptListFacade.CurrentDataset as TransactionReceiptDatasetGateway;
                var facadeTransactionReceipt = _facadeSalesOrder.TransactionReceiptListFacade as TransactionReceiptFacade;
                int datasourceRowIndex = 0;
                facadeTransactionReceipt.AssignTransactionReceipt(_gatewaySalesOrderDataset.CustomerSalesOrderView[0].SalesOrderCode, facadeReceipt, ref datasourceRowIndex);
                // Manually merge the Customer Transaction Receipt table..
                _gatewaySalesOrderDataset.Merge(gatewayTransactionReceiptDataset.CustomerTransactionReceiptView);
                SaveSalesOrder();
                facadeTransactionReceipt.ReserveAndAllocateReceipt(_gatewaySalesOrderDataset, null, Interprise.Framework.Base.Shared.Const.CUSTOMER_SALES_ORDER);

                if (!approved)
                {
                    if (status != AppLogic.ro_3DSecure)
                    {
                        facadeTransactionReceipt.Void(_facadeSalesOrder.TransactionType.ToString(), _gatewaySalesOrderDataset);
                        _facadeSalesOrder.VoidOrder();

                        SaveSalesOrder();
                    }
                }

                if (approved && convertToInvoiceAndCapture)
                {
                    ConvertToInvoiceAndCapture(gatewayReceiptDataset, facadeReceipt, receiptCode);
                }

                // **************************************************
                //  Explicit Disposal and dereferencing goes here...
                // **************************************************

                facadeCustomerReceiptList.Dispose();
                datasetCustomerReceipt.Dispose();
                customerReceiptBaseDataset.Dispose();

                gatewayReceiptDataset.Dispose();
                gatewayTransactionReceiptDataset.Dispose();
                facadeTransactionReceipt.Dispose();
                facadeReceipt.Dispose();

                facadeCustomerReceiptList = null;
                datasetCustomerReceipt = null;
                customerReceiptBaseDataset = null;

                gatewayReceiptDataset = null;
                gatewayTransactionReceiptDataset = null;
                facadeTransactionReceipt = null;
                facadeReceipt = null;
            }

            return status;
        }

        private void AttachGatewayAuthorization(ReceiptDatasetGateway gatewayReceiptDataset,
            ReceiptDatasetGateway.PaymentMethodViewRow rowPaymentMethod,
            GatewayResponse withGatewayAuthResponse,
            string receiptCode)
        {
            // MAP Interprise Needed Fields here..
            ReceiptDatasetGateway.CustomerCCAuthResponseRow rowResponse = gatewayReceiptDataset.CustomerCCAuthResponse.NewCustomerCCAuthResponseRow();
            rowResponse.BeginEdit();
            rowResponse.ResponseCode = SimpleFacade.Instance.GenerateDocumentCode(Interprise.Framework.Base.Shared.Enum.TransactionType.CCResponse.ToString());
            rowResponse.CustomerCode = _gatewaySalesOrderDataset.CustomerSalesOrderView[0].BillToCode;
            rowResponse.DocumentCode = gatewayReceiptDataset.CustomerCardPayment[0].CardPaymentCode;
            rowResponse.TransactionType = "Auth";
            //rowResponse.CreditCard = rowPaymentMethod.CreditCardOnFile;
            rowResponse.Amount = rowPaymentMethod.AmountPaidRate; 
            rowResponse.GatewayResponseCode = withGatewayAuthResponse.Status; 
            rowResponse.ResponseMsg = withGatewayAuthResponse.Details; 
            rowResponse.Message = withGatewayAuthResponse.Details; 
            rowResponse.Result = 0;
            rowResponse.AuthCode = withGatewayAuthResponse.AuthorizationCode; 
            rowResponse.Reference = withGatewayAuthResponse.AuthorizationTransID; 
            rowResponse.AVSResult = withGatewayAuthResponse.AVSResult; 
            rowResponse.CVResult = withGatewayAuthResponse.CV2Result; 
            rowResponse.TransactionCommand = withGatewayAuthResponse.TransactionCommandOut; 
            rowResponse.AuthorizationResult = withGatewayAuthResponse.TransactionResponse; 
            rowResponse.EndEdit();

            gatewayReceiptDataset.CustomerCCAuthResponse.AddCustomerCCAuthResponseRow(rowResponse);

            rowPaymentMethod.BeginEdit();
            rowPaymentMethod.CreditCardReference = withGatewayAuthResponse.AuthorizationTransID;
            rowPaymentMethod.CreditCardResponseCode = rowResponse.ResponseCode;
            rowPaymentMethod.CreditCardAuthorizationCode = withGatewayAuthResponse.AuthorizationCode;
            // NOTE:
            //  We only do auth only for now
            rowPaymentMethod.CreditCardIsAuthorized = true;
            rowPaymentMethod.CreditCardIsVoided = false;
            rowPaymentMethod.CreditCardIsCredited = false;
            rowPaymentMethod.EndEdit();
        }

        private void AssignDownloadableItemCustomerAssociations()
        {
            this.CartItems.Where(item => item.ItemType == Const.ITEM_TYPE_ELECTRONIC_DOWNLOAD)
                          .ForEach(item =>
            {
                var download = DownloadableItem.FindByItemCode(item.ItemCode);
                if (null != download)
                {
                    // allow this customer to download this item..
                    download.AddCustomer(ThisCustomer);
                }
            });
        }

        public string PlaceOrder(Gateway usingThisGateway,
            Address billingAddress,
            Address shippingAddress,
            ref string salesOrderCode,
            ref string receiptCode,
            bool clearTransaction,
            bool clearLineItems,
            bool is3DsecondAuth)
        {
            return PlaceOrder(usingThisGateway,
                billingAddress,
                shippingAddress,
                ref salesOrderCode,
                ref receiptCode,
                clearTransaction,
                clearLineItems,
                null,
                true,
                is3DsecondAuth);
        }

        public string PlaceOrder(Gateway usingThisGateway,
            Address billingAddress,
            Address shippingAddress,
            ref string salesOrderCode,
            ref string receiptCode,
            bool clearTransaction,
            bool clearLineItems,
            GatewayResponse response,
            bool applyPayment,
            bool is3DsecondAuth)
        {
            string status = AppLogic.ro_OK;

            //Change Transaction Type to Quote if "REQUEST QUOTE"
            if ((ThisCustomer.PaymentTermCode.ToUpper() == "REQUEST QUOTE"))
            {
                _facadeSalesOrder.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.Quote;
            }

            // NOTE:
            // A possible instance of salesOrderCode not being empty
            // is if it's coming from a 3DSecure callback
            if (CommonLogic.IsStringNullOrEmpty(salesOrderCode))
            {
                salesOrderCode = _facadeSalesOrder.GenerateDocumentCode(_facadeSalesOrder.TransactionType.ToString());
            }

            // NOTE:
            // A possible instance of receiptCode not being empty
            // is if it's coming from a 3DSecure callback            
            if (CommonLogic.IsStringNullOrEmpty(receiptCode))
            {
                receiptCode = SimpleFacade.Instance.GenerateDocumentCode(Interprise.Framework.Base.Shared.Enum.TransactionType.CustomerCardPayment.ToString());
            }

            if (!MaxMindCheck(salesOrderCode, billingAddress, shippingAddress))
            {
                return "MAXMIND FRAUD CHECK FAILED";
            }

            decimal getExchangeRate = _facadeSalesOrder.GetExchangerate(ThisCustomer.CurrencyCode);
            //recompute the freight charge
            ComputeFreightCharge(false, getExchangeRate);

            if (usingThisGateway != null)
            {
                decimal total = GetOrderTotal();
                bool useLiveTransactions = AppLogic.AppConfigBool("UseLiveTransactions");
                string cv2 = AppLogic.GetCardExtraCodeFromSession(ThisCustomer);

                response = usingThisGateway.ProcessCard(salesOrderCode,
                            ThisCustomer,
                            total,
                            receiptCode,
                            useLiveTransactions,
                            "AUTH",
                            billingAddress,
                            cv2,
                            shippingAddress,
                            string.Empty,
                            string.Empty,
                            string.Empty);

                cv2 = "1111111";

                status = response.Status;

                if (status == AppLogic.ro_3DSecure)
                {
                    return response.Status;
                }
                else if (status != AppLogic.ro_OK)
                {
                    // record failed transaction
                    var transactionFailed = FailedTransaction.New();
                    transactionFailed.CustomerCode = ThisCustomer.CustomerCode;
                    transactionFailed.SalesOrderCode = salesOrderCode;
                    transactionFailed.PaymentGateway = response.Gateway;
                    transactionFailed.PaymentMethod = InterpriseHelper.PAYMENT_METHOD_CREDITCARD;
                    transactionFailed.TransactionCommand = response.TransactionCommandOut;
                    transactionFailed.TransactionResult = response.TransactionResponse;
                    transactionFailed.IPAddress = ThisCustomer.LastIPAddress;
                    transactionFailed.IsFailed = true;

                    transactionFailed.Record();

                    if (AppLogic.AppConfigBool("ShowGatewayError"))
                    {
                        return AppLogic.ro_INTERPRISE_GATEWAY_AUTHORIZATION_FAILED;
                    }
                    else
                    {
                        return response.Status;
                    }
                }
            }

            TagOrder(salesOrderCode);
            AssignAffiliate();
            UpdateBillingAndShippingInformation(billingAddress, shippingAddress);
            SaveSalesOrderAndAllocate(salesOrderCode);

            if (_facadeSalesOrder.TransactionType != Interprise.Framework.Base.Shared.Enum.TransactionType.Quote)
            {
                // Promote to Customer if still prospect..
                InterpriseHelper.SetCustomerProspect(false, ThisCustomer.CustomerCode);
            }
            
            if (applyPayment)
            {
                bool convertToInvoiceAndCapture = "AUTH CAPTURE".Equals(AppLogic.AppConfig("TransactionMode"), StringComparison.InvariantCultureIgnoreCase);
                status = ProcessPayment(convertToInvoiceAndCapture, billingAddress, response, receiptCode, salesOrderCode, is3DsecondAuth);
            }

            if (status == AppLogic.ro_3DSecure)
            {
                return status;
            }

            if (status == AppLogic.ro_OK)
            {
                AssignDownloadableItemCustomerAssociations();
            }

            if (status != AppLogic.ro_OK)
            {
                return status;
            }

            if (status != AppLogic.ro_INTERPRISE_GATEWAY_AUTHORIZATION_FAILED)
            {
                if (clearTransaction)
                {
                    this.ClearTransaction();
                }

                if (clearLineItems)
                {
                    this.ClearLineItems();
                }
            }

            return status;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_gatewaySalesOrderDataset != null)
            {
                _gatewaySalesOrderDataset.Dispose();
            }

            if (_facadeSalesOrder != null)
            {
                _facadeSalesOrder.Dispose();
            }
        }

        #endregion

        private void ConvertToInvoiceAndCapture(ReceiptDatasetGateway gatewayReceiptDataset, ReceiptFacade facadeReceipt, string receiptCode)
        {
            _gatewaySalesOrderDataset.CustomerSalesOrderWorkflowView[0].Stage = Interprise.Framework.Customer.Shared.Const.WORKFLOW_STAGE_READY_TO_INVOICE;

            DataTable warehouseTable = _facadeSalesOrder.InitializeConvertion();
            _facadeSalesOrder.SelectAllItemsToInvoice(warehouseTable, true);

            InvoiceDatasetGateway tmpGatewayInvoiceDataset = null;
            string invoiceConversionErrorMessage = string.Empty;
            if (_facadeSalesOrder.ConvertSalesOrderToInvoice(
                    Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice,
                    ref tmpGatewayInvoiceDataset,
                    true,
                    false,
                    ref invoiceConversionErrorMessage))
            {
                if (null != tmpGatewayInvoiceDataset)
                {
                    using (var gatewayInvoiceDataset = new InvoiceDatasetGateway())
                    {
                        using (var facadeInvoice = new InvoiceFacade(gatewayInvoiceDataset))
                        {
                            facadeInvoice.TransactionType = Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice;

                            string convertedInvoiceCode = tmpGatewayInvoiceDataset.CustomerInvoiceView[0].InvoiceCode;

                            string[][] paramSet = new string[][] { new string[] { "@InvoiceCode", convertedInvoiceCode } };

                            //unified load of data to avoid PK related error during update.
                            facadeInvoice.LoadDataSet(Interprise.Framework.Base.Shared.StoredProcedures.READCUSTOMERINVOICEUNIFIED,
                            new string[] {tmpGatewayInvoiceDataset.CustomerInvoiceView.TableName, tmpGatewayInvoiceDataset.CustomerInvoiceDetailView.TableName, gatewayInvoiceDataset.TransactionTaxDetailView.TableName, 
                                      tmpGatewayInvoiceDataset.TransactionItemTaxDetailView.TableName, tmpGatewayInvoiceDataset.CustomerCreditAllocationView.TableName, tmpGatewayInvoiceDataset.CustomerCreditView.TableName,
                                      tmpGatewayInvoiceDataset.CustomerItemKitDetailView.TableName, tmpGatewayInvoiceDataset.CustomerSalesRepCommissionView.TableName, tmpGatewayInvoiceDataset.TransactionTaxSummary.TableName,
                                      tmpGatewayInvoiceDataset.CustomerInvoiceSerialLotNumbers.TableName, tmpGatewayInvoiceDataset.CustomerInvoiceKitSerialLotNumbers.TableName},
                            paramSet,
                            Interprise.Framework.Base.Shared.Enum.ClearType.None,
                            Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                            // let's reload the receipt table
                            string[][] reloadReceiptCommandset = new string[][]{
                                                                    new string[]{
                                                                        Const.CUSTOMERCARDPAYMENT_TABLE,
                                                                        StoredProcedures.READCUSTOMERCARDPAYMENT, 
                                                                        "@CardPaymentCode",
                                                                        receiptCode},
                                                                    new string[]{
                                                                        Const.PAYMENTMETHODVIEW_TABLE,
                                                                        StoredProcedures.READPAYMENTMETHOD,
                                                                        "@ReceivableCode",
                                                                        receiptCode}};

                            facadeReceipt.LoadDataSet(reloadReceiptCommandset, Interprise.Framework.Base.Shared.Enum.ClearType.Specific, Interprise.Framework.Base.Shared.Enum.ConnectionStringType.Online);

                            // this time capture the order....
                            gatewayReceiptDataset.PaymentMethodView[0].CreditCardTransactionType = "Capture";

                            var gatewayTransactionReceiptDatasetInvoice = facadeInvoice.TransactionReceiptListFacade.CurrentDataset as TransactionReceiptDatasetGateway;
                            var facadeTransactionReceiptInvoice = facadeInvoice.TransactionReceiptListFacade as TransactionReceiptFacade;

                            string gatewayMessage = string.Empty;
                            bool approved = false;

                            // we should re-load the card gateway processor
                            // just to make sure that we have it properly initialized clean
                            var cardGateway = GetGatewayProcessorCore();
                            using (var facadeCardCapture = new CreditCardAuthorizationFacade(cardGateway))
                            {
                                facadeCardCapture.TransactionType = facadeReceipt.TransactionType;
                                facadeCardCapture.TransactionReceiptFacade = facadeTransactionReceiptInvoice;
                                facadeCardCapture.CurrentCardPayment = receiptCode;
                                facadeCardCapture.CurrentParentTransactionCode = convertedInvoiceCode;
                                gatewayMessage = facadeCardCapture.AuthorizeCreditCard(facadeReceipt, false, ref approved);

                                if (approved)
                                {
                                    gatewayInvoiceDataset.Merge(gatewayTransactionReceiptDatasetInvoice.CustomerTransactionReceiptView);

                                    string errorCode = string.Empty;
                                    string[] invoiceRelatedTables = facadeInvoice.get_RelatedTables(Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice);
                                    string[][] invoiceCommands = facadeInvoice.CreateParameterSet(invoiceRelatedTables);

                                    facadeInvoice.EmailDownloadableItem(ref errorCode);
                                    facadeInvoice.ComputeTotals(false, false, false);
                                    //save current balance to DB before posting transaction.
                                    facadeInvoice.UpdateDataSet();
                                    string result = facadeInvoice.Post(string.Empty, string.Empty);

                                    if (gatewayInvoiceDataset.CustomerInvoiceView[0].IsPosted)
                                    {
                                        if (facadeInvoice.IsCreateBackOrder())
                                        {
                                            string boCodes = string.Empty;
                                            bool backOrderCreated = facadeInvoice.CreateBackOrder(ref boCodes);
                                        }

                                        if (facadeCardCapture.IsCurrentCardPaymentHasNewReceipt)
                                        {
                                            facadeReceipt.RefreshPaymentTable(facadeCardCapture.NewReceiptDataTable, facadeCardCapture.NewPaymentMethodDataTable);
                                            facadeTransactionReceiptInvoice.ReserveAndAllocateReceipt(gatewayInvoiceDataset, facadeReceipt, "Invoice");
                                            facadeInvoice.UpdateDataSet(invoiceCommands, Interprise.Framework.Base.Shared.Enum.TransactionType.Invoice, string.Empty, false);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        #region Gift Registry

        public bool HasRegistryItemsAndOneOrMoreItemsHasZeroInNeed()
        {
            return this.CartItems.AsQueryable()
                                .Where(ItemHasRegistryID())
                                .Any(item => item.RegistryItemQuantity == 0);
        }

        public bool HasRegistryItemsAndOneOrMoreItemsExceedsToTheInNeedQuantity()
        {
            return this.CartItems
                        .AsQueryable()
                        .Where(ItemHasRegistryID())
                        .Where(IsNotExistInRegistry())
                        .Any(item => item.HasRegistryItemQuantityConflict());
        }

        public bool HasRegistryItemButParentRegistryIsRemoved()
        {
            return CartItems.AsQueryable()
                            .Where(ItemHasRegistryID())
                            .Where(IsNotExistInRegistry())
                            .Any(IsNotParentRegistryExist());
        }

        public bool HasRegistryItemsRemovedFromRegistry()
        {
            return CartItems.AsQueryable()
                            .Where(ItemHasRegistryID())
                            .Where(IsNotExistInRegistry())
                            .Any(IsNotExistInRegistry());
        }

        public int RemoveRegistryItemsHasDeletedRegistry()
        {
            int totalItemToRemove = 0;
            if (!HasRegistryItems()) return totalItemToRemove;

            var itemsWithRemovedParentRegistry = CartItems
                                         .AsQueryable()
                                         .Where(ItemHasRegistryID())
                                         .Where(IsNotParentRegistryExist())
                                         .Select(item => string.Format("'{0}'", item.RegistryItemCode.ToString()))
                                         .ToArray()
                                         .AsParallel();

            totalItemToRemove = itemsWithRemovedParentRegistry.Count();
            if (totalItemToRemove > 0)
            {
                GiftRegistryDA.RemoveRegistryItems(string.Join(",", itemsWithRemovedParentRegistry));
            }
            return totalItemToRemove;
        }

        public int RemoveRegistryItemsHasBeenDeletedInRegistry()
        {
            int totalItemToRemove = 0;
            if (!HasRegistryItems()) return totalItemToRemove;

            var removedItems = CartItems.AsQueryable()
                                        .Where(ItemHasRegistryID())
                                        .Where(IsNotExistInRegistry())
                                        .Select(item => string.Format("'{0}'", item.RegistryItemCode.ToString()))
                                        .ToArray()
                                        .AsParallel();

            totalItemToRemove = removedItems.Count();
            if (totalItemToRemove > 0)
            {
                GiftRegistryDA.RemoveRegistryItems(string.Join(",", removedItems));
            }
            return totalItemToRemove;

        }

        public void RemoveRegistryItems()
        {
            var removedItems = CartItems
                                .AsQueryable()
                                .Where(ItemHasRegistryID())
                                .Select(item => string.Format("'{0}'", item.RegistryItemCode.ToString()))
                                .ToArray()
                                .AsParallel();
            GiftRegistryDA.RemoveRegistryItems(string.Join(",", removedItems));

        }

        public static Expression<Func<CartItem, bool>> IsNotParentRegistryExist()
        {
            Expression<Func<CartItem, bool>> exp = (item => !GiftRegistryDA.IsRegistryExist(item.GiftRegistryID.Value));
            return exp;
        }

        public static Expression<Func<CartItem, bool>> IsNotExistInRegistry()
        {
            Expression<Func<CartItem, bool>> exp = (item => !GiftRegistryDA.IsRegistryItemExists(item.RegistryItemCode.Value));
            return exp;
        }

        public static Expression<Func<CartItem, bool>> ItemHasRegistryID()
        {
            Expression<Func<CartItem, bool>> exp = (item => item.GiftRegistryID.HasValue);
            return exp;
        }

        #endregion

    }
}
